# Always print this out before your assignment
sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods  
[7] base     

other attached packages:
 [1] ggridges_0.5.3             glmnetUtils_1.1.8         
 [3] glmnet_4.1-3               Matrix_1.3-4              
 [5] plotROC_2.2.1              scales_1.1.1              
 [7] tidyquant_1.0.3            quantmod_0.4.18           
 [9] TTR_0.24.2                 PerformanceAnalytics_2.0.4
[11] xts_0.12.1                 zoo_1.8-9                 
[13] plotly_4.10.0              viridis_0.6.2             
[15] viridisLite_0.4.0          pastecs_1.3.21            
[17] kableExtra_1.3.4           lubridate_1.8.0           
[19] rsample_0.1.1              ggthemes_4.2.4            
[21] ggrepel_0.9.1              here_1.0.1                
[23] fs_1.5.0                   forcats_0.5.1             
[25] stringr_1.4.0              dplyr_1.0.7               
[27] purrr_0.3.4                readr_2.1.0               
[29] tidyr_1.1.4                tibble_3.1.6              
[31] ggplot2_3.3.5              tidyverse_1.3.1           
[33] knitr_1.36                

loaded via a namespace (and not attached):
 [1] colorspace_2.0-2  ellipsis_0.3.2    rprojroot_2.0.2  
 [4] rstudioapi_0.13   listenv_0.8.0     furrr_0.2.3      
 [7] farver_2.1.0      fansi_0.5.0       xml2_1.3.2       
[10] codetools_0.2-18  splines_4.1.2     jsonlite_1.7.2   
[13] broom_0.7.10      dbplyr_2.1.1      compiler_4.1.2   
[16] httr_1.4.2        backports_1.4.0   assertthat_0.2.1 
[19] fastmap_1.1.0     lazyeval_0.2.2    cli_3.1.0        
[22] htmltools_0.5.2   tools_4.1.2       gtable_0.3.0     
[25] glue_1.5.0        Rcpp_1.0.7        cellranger_1.1.0 
[28] jquerylib_0.1.4   vctrs_0.3.8       svglite_2.0.0    
[31] nlme_3.1-153      iterators_1.0.13  crosstalk_1.2.0  
[34] xfun_0.28         globals_0.14.0    rvest_1.0.2      
[37] lifecycle_1.0.1   future_1.23.0     hms_1.1.1        
[40] parallel_4.1.2    yaml_2.2.1        curl_4.3.2       
[43] gridExtra_2.3     sass_0.4.0        stringi_1.7.5    
[46] highr_0.9         foreach_1.5.1     boot_1.3-28      
[49] shape_1.4.6       rlang_0.4.12      pkgconfig_2.0.3  
[52] systemfonts_1.0.3 evaluate_0.14     lattice_0.20-45  
[55] htmlwidgets_1.5.4 labeling_0.4.2    tidyselect_1.1.1 
[58] parallelly_1.29.0 plyr_1.8.6        magrittr_2.0.1   
[61] R6_2.5.1          generics_0.1.1    DBI_1.1.1        
[64] pillar_1.6.4      haven_2.4.3       withr_2.4.2      
[67] mgcv_1.8-38       survival_3.2-13   modelr_0.1.8     
[70] crayon_1.4.2      Quandl_2.11.0     utf8_1.2.2       
[73] tzdb_0.2.0        rmarkdown_2.11    grid_4.1.2       
[76] readxl_1.3.1      data.table_1.14.2 reprex_2.0.1     
[79] digest_0.6.28     webshot_0.5.2     munsell_0.5.0    
[82] bslib_0.3.1       quadprog_1.5-8   
getwd()
[1] "/Users/ryanradcliff/Documents/BUS696/BROCODE_Final_Project"

# load all your libraries in this chunk 
library('tidyverse')
library("fs")
library('here')
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')
library('ggthemes')
library('kableExtra')
library('pastecs')
library('viridis')
library('plotly')
library('tidyquant')
library('scales')
library("gdata")
gdata: read.xls support for 'XLS' (Excel 97-2004) files
gdata: ENABLED.

gdata: read.xls support for 'XLSX' (Excel 2007+) files
gdata: ENABLED.

Attaching package: ‘gdata’

The following objects are masked from ‘package:xts’:

    first, last

The following objects are masked from ‘package:pastecs’:

    first, last

The following objects are masked from ‘package:dplyr’:

    combine, first, last

The following object is masked from ‘package:purrr’:

    keep

The following object is masked from ‘package:stats’:

    nobs

The following object is masked from ‘package:utils’:

    object.size

The following object is masked from ‘package:base’:

    startsWith
# note, do not run install.packages() inside a code chunk. install them in the console outside of a code chunk. 

Part 1 - Final Project Cleaning and Summary Statistics

1a) Loading data


#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space 
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

1b) Fixing the wonky DOB & Data cleanup


#(Birthdate and Age, ID as a number)adding DOB (Age/Spouse Age) in years columns and adding two fields for assignment and number of children and number of degrees
dataclean <- data %>%
  mutate(Birthdate = ifelse(Birthdate == "0001-01-01", NA, Birthdate)) %>%
  mutate(Birthdate = mdy(Birthdate)) %>%
  mutate(Age = as.numeric(floor(interval(start= Birthdate, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(Spouse.Birthdate = ifelse(Spouse.Birthdate == "0001-01-01", NA, Spouse.Birthdate)) %>%
  mutate(Spouse.Birthdate = mdy(Spouse.Birthdate)) %>%
  mutate(Spouse.Age = as.numeric(floor(interval(start= Spouse.Birthdate,
                                                end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(ID = as.numeric(ID)) %>% 
  mutate(Assignment_flag = ifelse(is.na(Assignment.Number), 0,1)) %>% 
  mutate( No_of_Children = ifelse(is.na(Child.1.ID),0,
                            ifelse(is.na(Child.2.ID),1,2)))%>%
 mutate(ID = as.numeric(ID)) %>% 
    mutate( nmb_degree = ifelse(is.na(Degree.Type.1),0,
                            ifelse(is.na(Degree.Type.2),1,2))) %>%
#conferral dates
  mutate(Conferral.Date.1 = ifelse(Conferral.Date.1 == "0001-01-01", NA, Conferral.Date.1)) %>%
  mutate(Conferral.Date.1 = mdy(Conferral.Date.1)) %>%
  mutate(Conferral.Date.1.Age = as.numeric(floor(interval(start= Conferral.Date.1, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Conferral.Date.2 = ifelse(Conferral.Date.2 == "0001-01-01", NA, Conferral.Date.2)) %>%
  mutate(Conferral.Date.2 = mdy(Conferral.Date.2)) %>%
  mutate(Conferral.Date.2.Age = as.numeric(floor(interval(start= Conferral.Date.2, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Last.Contact.By.Anyone = ifelse(Last.Contact.By.Anyone == "0001-01-01", NA, Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.By.Anyone = mdy(Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.Age = as.numeric(floor(interval(start= Last.Contact.By.Anyone, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
 mutate(HH.First.Gift.Date = ifelse(HH.First.Gift.Date == "0001-01-01", NA, HH.First.Gift.Date)) %>%
  mutate(HH.First.Gift.Date = mdy(HH.First.Gift.Date)) %>%
mutate(HH.First.Gift.Age = as.numeric(floor(interval(start= HH.First.Gift.Date, end=Sys.Date())/duration(n=1, unit="years")))) %>% 
#major gift 
  mutate(major_gifter = ifelse(Lifetime.Giving > 50000, 1,0) %>% factor(., levels = c("0","1"))) %>%
#splitting up the age into ranges and creating category for easy visualization 
  mutate(age_range = 
    ifelse(Age %in% 10:19, "10 < 20 years old",
    ifelse(Age %in% 20:29, "20 < 30 years old", 
    ifelse(Age %in% 30:39, "30 < 40 years old",
    ifelse(Age %in% 40:49, "40 < 50 years old",
    ifelse(Age %in% 50:59, "50 < 60 years old",
    ifelse(Age %in% 60:69, "60 < 70 years old",
    ifelse(Age %in% 70:79, "70 < 80 years old",
    ifelse(Age %in% 80:89, "80 < 90 years old",
    ifelse(Age %in% 90:120, "90+ years old",
    NA)))))))))) %>%
#creating a region column using the county data and the OMB MSA (Metropolitan Statistical Area) definitions
  mutate(region = 
    ifelse(County == "San Luis Obispo" & State == "CA", "So Cal",
    ifelse(County == "Kern" & State == "CA", "So Cal",
    ifelse(County == "San Bernardino" & State == "CA", "So Cal",
    ifelse(County == "Santa Barbara" & State == "CA", "So Cal",
    ifelse(County == "Ventura" & State == "CA", "So Cal",
    ifelse(County == "Los Angeles" & State == "CA", "So Cal",
    ifelse(County == "Orange" & State == "CA", "So Cal",
    ifelse(County == "Riverside" & State == "CA", "So Cal",
    ifelse(County == "San Diego" & State == "CA", "So Cal",
    ifelse(County == "Imperial" & State == "CA", "So Cal",
    ifelse(County == "King" & State == "WA", "Seattle",
    ifelse(County == "Snohomish" & State == "WA", "Seattle",
    ifelse(County == "Pierce" & State == "WA", "Seattle",
    ifelse(County == "Clackamas" & State == "OR", "Portland",
    ifelse(County == "Columbia" & State == "OR", "Portland",
    ifelse(County == "Multnomah" & State == "OR", "Portland",
    ifelse(County == "Washington" & State == "OR", "Portland",
    ifelse(County == "Yamhill" & State == "OR", "Portland",
    ifelse(County == "Clark" & State == "WA", "Portland",
    ifelse(County == "Skamania" & State == "WA", "Portland",
    ifelse(County == "Denver" & State == "CO", "Denver",
    ifelse(County == "Arapahoe" & State == "CO", "Denver",
    ifelse(County == "Jefferson" & State == "CO", "Denver",
    ifelse(County == "Adams" & State == "CO", "Denver",
    ifelse(County == "Douglas" & State == "CO", "Denver",
    ifelse(County == "Broomfield" & State == "CO", "Denver",    
    ifelse(County == "Elbert" & State == "CO", "Denver",
    ifelse(County == "Park" & State == "CO", "Denver",
    ifelse(County == "Clear Creek" & State == "CO", "Denver",
    ifelse(County == "Alameda" & State == "CA", "Bay Area",
    ifelse(County == "Contra Costa" & State == "CA", "Bay Area",
    ifelse(County == "Marin" & State == "CA", "Bay Area",
    ifelse(County == "Monterey" & State == "CA", "Bay Area",
    ifelse(County == "Napa" & State == "CA", "Bay Area",
    ifelse(County == "San Benito" & State == "CA", "Bay Area",
    ifelse(County == "San Francisco" & State == "CA", "Bay Area",
    ifelse(County == "San Mateo" & State == "CA", "Bay Area",
    ifelse(County == "Santa Clara" & State == "CA", "Bay Area",
    ifelse(County == "Santa Cruz" & State == "CA", "Bay Area",
    ifelse(County == "Solano" & State == "CA", "Bay Area",
    ifelse(County == "Sonoma" & State == "CA", "Bay Area",
           NA)))))))))))))))))))))))))))))))))))))))))) %>%
  mutate(region = 
    ifelse(County == "Kings" & State == "NY", "New York",
    ifelse(County == "Queens" & State == "NY", "New York",
    ifelse(County == "New York" & State == "NY", "New York",
    ifelse(County == "Bronx" & State == "NY", "New York",
    ifelse(County == "Richmond" & State == "NY", "New York",
    ifelse(County == "Westchester" & State == "NY", "New York",
    ifelse(County == "Bergen" & State == "NY", "New York",
    ifelse(County == "Hudson" & State == "NY", "New York",
    ifelse(County == "Passaic" & State == "NY", "New York",
    ifelse(County == "Putnam" & State == "NY", "New York",
    ifelse(County == "Rockland" & State == "NY", "New York",
    ifelse(County == "Suffolk" & State == "NY", "New York",
    ifelse(County == "Nassau" & State == "NY", "New York",
    ifelse(County == "Middlesex" & State == "NJ", "New York",
    ifelse(County == "Monmouth" & State == "NJ", "New York",
    ifelse(County == "Ocean" & State == "NJ", "New York",
    ifelse(County == "Somerset" & State == "NJ", "New York",
    ifelse(County == "Essex" & State == "NJ", "New York",
    ifelse(County == "Union" & State == "NJ", "New York",
    ifelse(County == "Morris" & State == "NJ", "New York",
    ifelse(County == "Sussex" & State == "NJ", "New York",
    ifelse(County == "Hunterdon" & State == "NJ", "New York",
    ifelse(County == "Pike" & State == "NJ", "New York",
    region)))))))))))))))))))))))) %>%

# code nor cal region as all others in CA not already defined
  mutate(region = 
    ifelse(State == "CA" & is.na(region) == TRUE, "Nor Cal", region))

#Initial Removal of Columns that provide no benefit 

dataclean <- subset(dataclean,select = -c(Assignment.Number
                                                    ,Assignment.has.Historical.Mngr
                                                    ,Suffix
                                                    ,Assignment.Date
                                                    ,Assignment.Manager
                                                    ,Assignment.Role
                                                    ,Assignment.Title
                                                    ,Assignment.Status
                                                    ,Strategy
                                                    ,Progress.Level
                                                    ,Assignment.Group
                                                    ,Assignment.Category
                                                    ,Funding.Method
                                                        ,Expected.Book.Date
                                                        ,Qualification.Amount
                                                        ,Expected.Book.Amount
                                                        ,Expected.Book.Date
                                                        ,Hard.Gift.Total
                                                        ,Soft.Credit.Total
                                                        ,Total.Assignment.Gifts
                                                        ,No.of.Pledges
                                                        ,Proposal..
                                                        ,Proposal.Notes
                                                        ,HH.Life.Spouse.Credit
                                                        ,Last.Contact.By.Manager
                                                        ,X..of.Contacts.By.Manager
                                                        ,DonorSearch.Range
                                                        ,iWave.Range
                                                        ,WealthEngine.Range
                                                        ,Philanthropic.Commitments
                                                        ))
#cleaning up zip codes removing -4 after 
dataclean$Zip <- gsub(dataclean$Zip, pattern="-.*", replacement = "")

#adding zip code data and column 
zip <- read.csv(here::here("final_project", "Salary_Zipcode.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")


#adding zip salary column
dataclean <-dataclean %>%
    mutate(zipcode_slry = VLOOKUP(Zip, zip, NAME, S1902_C03_002E)) %>%
#slry range 
  mutate(zipslry_range = 
    ifelse(zipcode_slry %in% 10000:89999, "90K-99K",
    ifelse(zipcode_slry %in% 90000:99999, "90K-99K",
    ifelse(zipcode_slry %in% 100000:149999, "100K-149K", 
    ifelse(zipcode_slry %in% 150000:199999, "150K-199K",
    ifelse(zipcode_slry %in% 200000:249999, "200K-249K",
    ifelse(zipcode_slry %in% 250000:299999, "250K-299K",
    ifelse(zipcode_slry %in% 300000:349999, "300K-349K",
    ifelse(zipcode_slry %in% 350000:399999, "350K-399K",
    ifelse(zipcode_slry %in% 400000:499999, "400K-499K",
    ifelse(zipcode_slry %in% 500000:999999, "500K-999K",
    NA)))))))))))

sum(is.na(dataclean$zipcode_slry))
[1] 62347
#adding scholarship data (y/n)
schlr <- read.csv(here::here("final_project", "scholarship.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

#adding scholarship column
dataclean <-dataclean %>%
    mutate(scholarship = VLOOKUP(ID, schlr, ID, SCHOLARSHIP)) 

#replacing NA with 0 
 dataclean$scholarship <- replace_na(dataclean$scholarship,'0')
 
#replacing Y with 1 
dataclean$scholarship<-ifelse(dataclean$scholarship=="Y",1,0)

#checking how many are N
table(dataclean$scholarship)

     0      1 
295264  27962 
#checking and deleting scholarship column 
class(dataclean$schlr_fct)
[1] "NULL"
dataclean = subset(dataclean, select = -c(scholarship))
  
#checking for duplicates N >1 indicates a records values are in the file twice 
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))

#removing duplicated records
dataclean <- unique(dataclean)

#Verifying n = 1 no ID with multiple records cleaned of dupes
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))
NA

1d Creating many many factor variables


dataclean <- 
  dataclean %>% 
  #SEX
  mutate(sex_fct = 
           fct_explicit_na(Sex),
sex_simple = 
    fct_lump_n(Sex, n = 4),
#MARRIED
married_fct = 
           fct_explicit_na(Married),
  #DONOR SEGMENT
  donorseg_fct = 
           fct_explicit_na(Donor.Segment),
         donorseg_simple = 
           fct_lump_n(Donor.Segment, n = 4),
  #CONTACT RULE
         contact_fct = 
           fct_explicit_na(Contact.Rules),
         contact_simple = 
           fct_lump_n(Contact.Rules, n = 4),
  #SPOUSE MAIL
         spomail_fct = 
           fct_explicit_na(Spouse.Mail.Rules),
         spomail_simple = 
           fct_lump_n(Spouse.Mail.Rules, n = 4),
  #JOB TITLE
         jobtitle_fct = 
           fct_explicit_na(Job.Title),
         jobtitle_simple = 
           fct_lump_n(Job.Title, n = 5),
  #DEGREE TYPE 1
         deg1_fct = 
           fct_explicit_na(Degree.Type.1),
         deg1_simple = 
           fct_lump_n(Degree.Type.1, n = 5),
  #DEGREE TYPE 2
         deg2_fct = 
           fct_explicit_na(Degree.Type.2),
         deg2_simple = 
           fct_lump_n(Degree.Type.2, n = 5),
  #MAJOR 1
         maj1_fct = 
           fct_explicit_na(Major.1),
         maj1_simple = 
           fct_lump_n(Major.1, n = 5),
  #MAJOR 2
         maj2_fct = 
           fct_explicit_na(Major.2),
         maj2_simple = 
           fct_lump_n(Major.2, n = 5),
  #MINOR 1
         min1_fct = 
           fct_explicit_na(Minor.1),
         min1_simple = 
           fct_lump_n(Minor.1, n = 5),
  #MINOR 2
         min2_fct = 
           fct_explicit_na(Minor.2),
         min2_simple = 
           fct_lump_n(Minor.2, n = 5),
  #SCHOOL 1
         school1_fct = 
           fct_explicit_na(School.1),
         school1_simple = 
           fct_lump_n(School.1, n = 5),
  #SCHOOL 2
         school2_fct = 
           fct_explicit_na(School.2),
         school2_simple = 
           fct_lump_n(School.2, n = 5),
  #INSTITUTION TYPE
         insttype_fct = 
           fct_explicit_na(Institution.Type),
         insttype_simple = 
           fct_lump_n(Institution.Type, n = 5),
  #EXTRACURRICULAR
         extra_fct = 
           fct_explicit_na(Extracurricular),
         extra_simple = 
           fct_lump_n(Extracurricular, n = 5),
  #HH FIRST GIFT FUND
         hhfirstgift_fct = 
           fct_explicit_na(HH.First.Gift.Fund),
         hhfirstgift_simple = 
           fct_lump_n(HH.First.Gift.Fund, n = 5),
#CHILD 1 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.1.Enroll.Status),
         ch1_enroll_simple = 
           fct_lump_n(Child.1.Enroll.Status, n = 4),
#CHILD 1 MAJOR
         ch1_maj_fct = 
           fct_explicit_na(Child.1.Major),
         ch1_maj_simple = 
           fct_lump_n(Child.1.Major, n = 4),
#CHILD 1 MINOR
         ch1_min_fct = 
           fct_explicit_na(Child.1.Minor),
         ch1_min_simple = 
           fct_lump_n(Child.1.Minor, n = 4),
#CHILD 1 SCHOOL
         ch1_school_fct = 
           fct_explicit_na(Child.1.School),
         ch1_school_simple = 
           fct_lump_n(Child.1.School, n = 4),
#CHILD 1 FEEDER
         ch1_feeder_fct = 
           fct_explicit_na(Child.1.Feeder.School),
         ch1_feeder_simple = 
           fct_lump_n(Child.1.Feeder.School, n = 4),
#CHILD 2 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.2.Enroll.Status),
         ch2_enroll_simple = 
           fct_lump_n(Child.2.Enroll.Status, n = 4),
#CHILD 2 MAJOR
         ch2_maj_fct = 
           fct_explicit_na(Child.2.Major),
         ch2_maj_simple = 
           fct_lump_n(Child.2.Major, n = 4),
#CHILD 2 MINOR
         ch2_min_fct = 
           fct_explicit_na(Child.2.Minor),
         ch2_min_simple = 
           fct_lump_n(Child.2.Minor, n = 4),
#CHILD 2 SCHOOL
         ch2_school_fct = 
           fct_explicit_na(Child.2.School),
         ch2_school_simple = 
           fct_lump_n(Child.2.School, n = 4),
#CHILD 2 FEEDER
         ch2_feeder_fct = 
           fct_explicit_na(Child.2.Feeder.School),
         ch2_feeder_simple = 
           fct_lump_n(Child.2.Feeder.School, n = 4),
    )



#checking to see if its a factor
#class(dataclean$sex_fct)
#class(dataclean$donorseg_fct)
#class(dataclean$contact_fct)
#class(dataclean$spomail_fct)

#checking levels
#levels(dataclean$sex_simple)
#levels(dataclean$donorseg_simple)
#levels(dataclean$contact_simple)
#levels(dataclean$spomail_simple)
#levels(dataclean$hhfirstgift_simple)

#creating a table against Sex column 
#table(dataclean$sex_fct, dataclean$sex_simple)

Region Analysis

#grouping by region and analyzing 
dataclean %>%
  group_by(region) %>%
  summarise(Count = length(region),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  mutate(mean_total_giv = dollar(mean_total_giv)) %>%
  kable(col.names = c("Region", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Region Count Mean HH Lifetime Giving
So Cal 145139 $5,090.84
NA 130306 $2,040.98
Bay Area 20641 $755.92
Nor Cal 10707 $3,823.63
Seattle 5425 $922.08
New York 4959 $1,978.49
Portland 2976 $1,098.24
Denver 2847 $257.29
NA
NA

DonorSegment Analysis

#grouping by donorsegment and analyzing 
dataclean %>%
  group_by(Donor.Segment) %>%
  summarise(Count = length(Donor.Segment),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  #added scales package to have the values show in dollar 
  mutate(mean_total_giv = (dollar(mean_total_giv))) %>%
  kable(col.names = c("Donor Segment", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Donor Segment Count Mean HH Lifetime Giving
NA 231974 $0.00
Lost Donor 69718 $4,954.47
Lapsed Donor 11193 $10,069.79
Current Donor 5603 $90,638.32
Lapsing Donor 3862 $16,590.15
At-Risk Donor 650 $77,143.93
NA
NA

First gift size

aq <- quantile(dataclean$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

aq <- as.data.frame(aq)

aq$aq <- dollar(aq$aq)

aq %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% $0.00
50% $0.00
75% $0.00
90% $40.00
99% $1,910.06
NA
NA

Consecutive giving

#consecutive years of giving 
dataclean %>%
  filter(Max.Consec.Fiscal.Years > 0) %>%
  ggplot(aes(Max.Consec.Fiscal.Years)) + geom_histogram(fill = "#002845", bins = 20) + 
  theme_economist_white() +
  ggtitle("Consecutive Years of Giving Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,2)) +
  scale_y_continuous(breaks = seq(0,10000000,5000)) 

NA
NA
NA

Lifetime giving based on number of children

dataclean %>%
  filter(HH.Lifetime.Giving <= 10000) %>%
  filter(HH.Lifetime.Giving > 0) %>%
  mutate(`No_of_Children` = as.factor(`No_of_Children`)) %>%
  ggplot(aes(HH.Lifetime.Giving, fill = `No_of_Children`)) + geom_histogram(bins = 30) + theme_economist_white() +
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,100000,1000)) +
  scale_y_continuous(breaks = seq(0,100000000,5000)) +
  ggtitle("Giving distribution and number of children")+ 
  scale_fill_manual(values=c("#002845", "#00cfcc", "#ff9973"))

NA
NA
NA

Mean, Median, and Count of Giving in Age Ranges


age_range_giving <- dataclean %>%
  group_by(age_range) %>%
  summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
            med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
            amount_of_people_in_age_range = n())


glimpse(age_range_giving)
Rows: 10
Columns: 4
$ age_range                     <chr> "10 < 20 years old", "20 < 30 …
$ avg_giving                    <dbl> 0.4464244, 28.2686916, 386.687…
$ med_giving                    <dbl> 0, 0, 0, 0, 0, 0, 0, 10, 15, 0
$ amount_of_people_in_age_range <int> 3977, 24556, 21022, 16834, 207…

Part 2 - Summary Statistics

2a) Plotting average giving by age range


age_range_giving <-
  age_range_giving %>%
  mutate(age_range = factor(age_range))

ggplot(age_range_giving, aes(age_range, avg_giving)) +
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + labs(x = "Age Range", y = "Average Giving") +
      ggtitle("Average Giving Compared Across Age Ranges")

NA
NA

2b) Count of donors based on age range (another way to look at it)


ggplot(dataclean, 
       aes(age_range)) + 
       geom_bar() + 
       theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + 
  labs(title = "Count of Age Ranges", x = "", y = "")

NA
NA

2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.


ggplot(dataclean, aes(age_range,HH.Lifetime.Giving,fill = age_range)) + 
  geom_boxplot(
  outlier.colour = "red") + 
  scale_y_log10() +
  theme(axis.text.x=element_text(angle=45,hjust=1))  + labs(x = "Age Range", y = "Lifetime Giving Amount") +
      ggtitle("Lifetime Giving Compared Across Age Ranges")

NA
NA

2d) Splitting by age and gender



#creating boxplots 
dataclean %>% 
  filter(Age < 100) %>% #removing the weird outliers that are over 100 
  filter(Sex %in% c("M", "F")) %>%
  ggplot(aes(Sex, Age)) + 
  geom_boxplot() + 
  theme_economist() + 
  ggtitle("Ages of Donors Based on Gender") + 
  xlab(NULL) + ylab(NULL)
  

Giving by gender


#remove NAs U X

dataclean2 <- dataclean %>%
  filter(Sex %in% c("M", "F")) 

q <- ggplot(dataclean2) 
q + stat_summary_bin(
  aes(y = HH.Lifetime.Giving, x = Sex), 
  fun.y = "mean", geom = "bar") 
  
summary(dataclean$sex_simple)

Mean age by gender

#breakdown of sexs 
tally(group_by(dataclean, Sex))

summarize(group_by(dataclean, Sex), 
          avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
          avg_age = mean(Age, na.rm = TRUE),
          med_age = median(Age, na.rm = TRUE))

#grouping by sex and age range for slides 
tally(group_by(dataclean, Sex, age_range))

2e) Distribution of people in the states that they live.


  dataclean %>%
  mutate(State = ifelse(State == " ", "NA", State)) %>%
  filter(State != "NA") %>%
  group_by(State) %>%
  summarise(Count = length(State)) %>%
  filter(Count > 800) %>%
  arrange(-Count) %>%
  kable(col.names = c("Donor's State", "Count")) %>%
  kable_styling(bootstrap_options = c("condensed"),
                full_width = F)
Donor's State Count
CA 176487
WA 7957
TX 7266
NY 5659
CO 5073
AZ 4925
OR 4612
FL 4111
IL 3681
HI 3394
PA 2904
OH 2754
NV 2715
MI 2523
MA 2473
NJ 2311
VA 2158
NC 2085
GA 2044
MO 1889
MN 1732
MD 1488
TN 1443
IN 1417
CT 1380
WI 1330
UT 1173
OK 1151
AL 1120
LA 1110
ID 1096
SC 1076
KY 1032
KS 1027
NM 981
IA 880
NA
NA

2f) Looking at all donors first gift amount. 75% made a first gift of <100.


 no_non_donors <- dataclean %>%
  filter(Lifetime.Giving != 0)
  
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

nd <- as.data.frame(nd)

nd %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
  
  

Part 3 - Modeling

Split data and create a new set for easier analysis


#converting married Y and N to 1 and 0 
dataclean <- dataclean %>%
      mutate(Married_simple = ifelse(Married == "N",0,1))


dataclean <- dataclean %>%
  mutate(hh.lifetime.giving_fct = as.factor(HH.Lifetime.Giving)) %>%
  mutate(HH.Lifetime.Giving.Plus = log(HH.Lifetime.Giving + 1))



#Creating the velvet set - only the best can enter

datavelvet <- subset(dataclean,select = -c(
  Category.Codes,
  Category.Descriptions,
  Contact.Rules,
  County,
  Degree.Type.1,
  Degree.Type.2,
  Donor.Segment,
  Conferral.Date.1,
  Conferral.Date.2,
  Contact.Rules,
  Degree.Type.1,
  Degree.Type.2,
  Extracurricular,
  HH.First.Gift.Fund,
  ID,
  Institution.Type,
  Job.Title,
  Last.Contact.By.Anyone,
  Major.1,
  Major.2,
  School.1,
  School.2,
  Spouse.Birthdate,
  Spouse.Mail.Rules,
  Zip
))



library("rsample")

data_split <- initial_split(datavelvet, prop = 0.75)

data_train <- training(data_split)
data_test <- testing(data_split)
p <- datavelvet %>%
  ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
  scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))
Scale for 'x' is already present. Adding another scale for 'x',
which will replace the existing scale.
ggplotly(p)
  
p


ggplot(data = datavelvet, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

NA
NA
NA

Another Histogram


datavelvet %>%
  filter(Age >= 10) %>%
  filter(Age <= 90) %>%
  ggplot(aes(Age)) + geom_histogram(fill = "#002845", bins = 20) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,10000000,2000)) 

Age distribution by gender

#Age Gender filtered out below 15 and above 90 - also removed U X the weird values 
datavelvet %>%
  filter(Age >= 15) %>%
  filter(Age <= 90) %>%
  mutate(Sex = as.factor(Sex)) %>%
  filter(Sex != "U") %>%
  filter(Sex != "X") %>%
  ggplot(aes(Age, fill = Sex)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Age Distribution by Gender") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,10)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Donor age distribution by marital status

#Age Marital Status
dataclean %>%
  filter(Age >= 20) %>%
  filter(Age <= 85) %>%
  ggplot(aes(Age, fill = Married)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution by Marital Status") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Linear Model

#These will focus on predicting whether a constituent is a donor or non-donor. 


mod1lm <- lm( Lifetime.Giving ~ Married_simple,
           data = data_train)

mod2lm <- lm( Total.Giving.Years ~ Lifetime.Giving,
           data = data_train)

mod3lm <- lm( Lifetime.Giving ~ region,
           data = data_train)

summary(mod1lm)

Call:
lm(formula = Lifetime.Giving ~ Married_simple, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -2867    -2742    -2661    -2661 18111464 

Coefficients:
               Estimate Std. Error t value            Pr(>|t|)    
(Intercept)      2660.9      251.3  10.588 <0.0000000000000002 ***
Married_simple    205.9      469.1   0.439               0.661    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 104400 on 242248 degrees of freedom
Multiple R-squared:  7.953e-07, Adjusted R-squared:  -3.333e-06 
F-statistic: 0.1927 on 1 and 242248 DF,  p-value: 0.6607
summary(mod2lm)

Call:
lm(formula = Total.Giving.Years ~ Lifetime.Giving, data = data_train)

Residuals:
    Min      1Q  Median      3Q     Max 
-36.600  -0.554  -0.554  -0.554  39.403 

Coefficients:
                     Estimate    Std. Error t value
(Intercept)     0.55445026328 0.00396511550  139.83
Lifetime.Giving 0.00000343205 0.00000003795   90.43
                           Pr(>|t|)    
(Intercept)     <0.0000000000000002 ***
Lifetime.Giving <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.951 on 242248 degrees of freedom
Multiple R-squared:  0.03266,   Adjusted R-squared:  0.03265 
F-statistic:  8178 on 1 and 242248 DF,  p-value: < 0.00000000000000022
summary(mod3lm)

Call:
lm(formula = Lifetime.Giving ~ region, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -3977    -3968    -3968    -3598 18110156 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)       513.0      950.9   0.539 0.589558    
regionDenver     -367.7     2739.3  -0.134 0.893220    
regionNew York   1954.2     2160.7   0.904 0.365769    
regionNor Cal    3464.0     1623.1   2.134 0.032826 *  
regionPortland    161.0     2680.2   0.060 0.952111    
regionSeattle    -128.2     2088.2  -0.061 0.951057    
regionSo Cal     3455.5     1016.1   3.401 0.000672 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 118200 on 144684 degrees of freedom
  (97559 observations deleted due to missingness)
Multiple R-squared:  0.0001214, Adjusted R-squared:  7.989e-05 
F-statistic: 2.927 on 6 and 144684 DF,  p-value: 0.007435
#increasing the giving year one year increase total giving by 0.0035


ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~region) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Region")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~nmb_degree) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Number of Degrees")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.First.Gift.Amount))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~donorseg_fct) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Donor Segment")
`geom_smooth()` using formula 'y ~ x'

#This plot actually has some interesting results
ggplot(data = data_train, aes(x = Age, y = log(Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~No_of_Children) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("# Children")
`geom_smooth()` using formula 'y ~ x'

data_train %>% 
  select_if(is.factor) %>% 
  glimpse()
Rows: 242,250
Columns: 54
$ major_gifter           <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ sex_fct                <fct> F, M, F, M, (Missing), M, M, (Missing…
$ sex_simple             <fct> F, M, F, M, NA, M, M, NA, M, NA, M, M…
$ married_fct            <fct> Y, Y, N, N, N, N, N, N, N, N, N, N, N…
$ donorseg_fct           <fct> Lost Donor, (Missing), (Missing), (Mi…
$ donorseg_simple        <fct> Lost Donor, NA, NA, NA, NA, Lost Dono…
$ contact_fct            <fct> No Solicitations, (Missing), (Missing…
$ contact_simple         <fct> No Solicitations, NA, NA, NA, NA, No …
$ spomail_fct            <fct> No Solicitations, (Missing), (Missing…
$ spomail_simple         <fct> No Solicitations, NA, NA, NA, NA, NA,…
$ jobtitle_fct           <fct> (Missing), Manager, (Missing), Public…
$ jobtitle_simple        <fct> NA, Other, NA, Other, NA, NA, NA, NA,…
$ deg1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg1_simple            <fct> NA, NA, NA, NA, NA, Bachelor of Arts,…
$ deg2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg2_simple            <fct> NA, NA, NA, NA, NA, Master of Arts, N…
$ maj1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj1_simple            <fct> NA, NA, NA, NA, NA, Other, Law (Full-…
$ maj2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj2_simple            <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ min1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min1_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ min2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min2_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ school1_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school1_simple         <fct> NA, NA, NA, NA, NA, NA, Other, NA, NA…
$ school2_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school2_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ insttype_fct           <fct> (Missing), (Missing), (Missing), (Mis…
$ insttype_simple        <fct> NA, NA, NA, NA, NA, NA, Law JD Full-T…
$ extra_fct              <fct> (Missing), (Missing), (Missing), (Mis…
$ extra_simple           <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ hhfirstgift_fct        <fct> (Missing), (Missing), (Missing), (Mis…
$ hhfirstgift_simple     <fct> NA, NA, NA, NA, NA, Pre-SRN Conversio…
$ ch1_enroll_fct         <fct> (Missing), Program Completed, (Missin…
$ ch1_enroll_simple      <fct> NA, NA, NA, NA, NA, Program Completed…
$ ch1_maj_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_maj_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_min_simple         <fct> NA, NA, NA, NA, NA, Non-Degree: GR Ta…
$ ch1_school_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_school_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_feeder_fct         <fct> (Missing), Palm Beach State College, …
$ ch1_feeder_simple      <fct> NA, Other, NA, NA, NA, NA, NA, NA, NA…
$ ch2_enroll_simple      <fct> NA, Program Completed, NA, NA, NA, NA…
$ ch2_maj_fct            <fct> (Missing), Business Administration BS…
$ ch2_maj_simple         <fct> NA, Business Administration BS, NA, N…
$ ch2_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_min_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch2_school_fct         <fct> (Missing), George L. Argyros School o…
$ ch2_school_simple      <fct> NA, George L. Argyros School of Busin…
$ ch2_feeder_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_feeder_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ hh.lifetime.giving_fct <fct> 25, 0, 0, 0, 0, 8048.75, 0, 0, 0, 0, …

MORE MODELS

Big logistic model


# Set family to binomial to set logistic function
# Run the model on the training set

donor_logit1 <-
  glm(hh.lifetime.giving_fct ~ Married_simple,
      family = "binomial",
      data = data_train)

summary(donor_logit1)


donor_logit2 <-
  glm(hh.lifetime.giving_fct ~ No_of_Children,
      family = "binomial",
      data = data_train)

summary(donor_logit2)


#summary(data_train$major_gifter)
#Assignment_flag taken out - may add back

donor_logit3 <-
  glm(major_gifter ~ Married + No_of_Children + donorseg_simple +  Total.Giving.Years + nmb_degree,
      family = "binomial",
      data = data_train)

summary(donor_logit3)

Call:
glm(formula = major_gifter ~ Married + No_of_Children + donorseg_simple + 
    Total.Giving.Years + nmb_degree, family = "binomial", data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.9550  -0.1321  -0.1193  -0.0669   4.1595  

Coefficients:
                              Estimate Std. Error z value
(Intercept)                  -3.847311   0.247574 -15.540
MarriedY                     -1.158717   0.088707 -13.062
No_of_Children                0.557000   0.061534   9.052
donorseg_simpleCurrent Donor  0.146362   0.251061   0.583
donorseg_simpleLapsed Donor  -0.489878   0.259079  -1.891
donorseg_simpleLapsing Donor -0.095711   0.269188  -0.356
donorseg_simpleLost Donor    -1.096049   0.246522  -4.446
Total.Giving.Years            0.201352   0.005549  36.289
nmb_degree                   -2.358069   0.142842 -16.508
                                         Pr(>|z|)    
(Intercept)                  < 0.0000000000000002 ***
MarriedY                     < 0.0000000000000002 ***
No_of_Children               < 0.0000000000000002 ***
donorseg_simpleCurrent Donor               0.5599    
donorseg_simpleLapsed Donor                0.0586 .  
donorseg_simpleLapsing Donor               0.7222    
donorseg_simpleLost Donor              0.00000875 ***
Total.Giving.Years           < 0.0000000000000002 ***
nmb_degree                   < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9589.0  on 68285  degrees of freedom
Residual deviance: 7109.5  on 68277  degrees of freedom
  (173964 observations deleted due to missingness)
AIC: 7127.5

Number of Fisher Scoring iterations: 8
exp(donor_logit3$coefficients)
                 (Intercept)                     MarriedY 
                  0.02133704                   0.31388876 
              No_of_Children donorseg_simpleCurrent Donor 
                  1.74542779                   1.15761478 
 donorseg_simpleLapsed Donor donorseg_simpleLapsing Donor 
                  0.61270093                   0.90872693 
   donorseg_simpleLost Donor           Total.Giving.Years 
                  0.33418885                   1.22305576 
                  nmb_degree 
                  0.09460272 
#training predictions for in sample preds 
preds_train <- predict(donor_logit3, newdata = data_train, type = "response") 

#test predicts for OOS (out of sample)
preds_test <- predict(donor_logit3, newdata = data_test, type = "response")

head(preds_train)
     110392        4746      150821       64568      127663 
0.010553848          NA 0.007096588          NA 0.008645723 
     295244 
0.073108492 
head(preds_test)
          9          12          16          18          26 
0.004078176 0.014993853 0.029756642 0.002233217 0.009722203 
         27 
0.006439700 
results_train <- data.frame(
  `truth` = data_train   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_train,
  `type` = rep("train",length(preds_train))
)

results_test <- data.frame(
  `truth` = data_test   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_test,
  `type` = rep("test",length(preds_test))
)

results <- bind_rows(results_train,results_test)

dim(results_train)
[1] 242250      3
dim(results_test)
[1] 80750     3
dim(results)
[1] 323000      3
library('plotROC')

p_plot <-
  ggplot(results,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 2.5,
           #Took the labelsize down to avoid cutoff
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 #We removed some of the cutoffs to avoid the mashup near the origin.

  #Changed the theme to avoid cutoff plot values.
  theme_classic(base_size = 14) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")
print(p_plot) 



p_train <-
  ggplot(results_train,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

p_test <-
  ggplot(results_test,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

#summary(donor_logit3)
#coef(donor_logit3)


#Calculating AUC of both
print(calc_auc(p_train)$AUC)
[1] 0.887512
print(calc_auc(p_test)$AUC)
[1] 0.8976386

RIDGE


library('glmnet')
library('glmnetUtils')

ridge_fit1 <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + donorseg_fct + No_of_Children,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit1)
print(ridge_fit1$lambda.min)

print(ridge_fit1$lambda.1se)
plot(ridge_fit1)

BIG RIDGE

data_train %>% map(levels) %>% map(length)
$ID
[1] 0

$Birthdate
[1] 0

$HH.Total.Gifts.FY20.21
[1] 0

$HH.Total.Gifts.FY19.20
[1] 0

$HH.Total.Gifts.FY18.19
[1] 0

$HH.Total.Gifts.FY17.18
[1] 0

$HH.Total.Gifts.FY16.17
[1] 0

$Class.Year
[1] 0

$Category.Codes
[1] 0

$Category.Descriptions
[1] 0

$Spouse.Birthdate
[1] 0

$Spouse.Class.Year
[1] 0

$Sex
[1] 0

$Married
[1] 0

$City
[1] 0

$State
[1] 0

$Zip
[1] 0

$County
[1] 0

$Job.Title
[1] 0

$Degree.Type.1
[1] 0

$Degree.Type.2
[1] 0

$Conferral.Date.1
[1] 0

$Conferral.Date.2
[1] 0

$Major.1
[1] 0

$Major.2
[1] 0

$Minor.1
[1] 0

$Minor.2
[1] 0

$School.1
[1] 0

$School.2
[1] 0

$Institution.Type
[1] 0

$Athletics
[1] 0

$Extracurricular
[1] 0

$Child.1.ID
[1] 0

$Child.1.Class.Year
[1] 0

$Child.1.Enroll.Status
[1] 0

$Child.1.Major
[1] 0

$Child.1.Minor
[1] 0

$Child.1.School
[1] 0

$Child.1.Feeder.School
[1] 0

$Child.2.ID
[1] 0

$Child.2.Class.Year
[1] 0

$Child.2.Enroll.Status
[1] 0

$Child.2.Major
[1] 0

$Child.2.Minor
[1] 0

$Child.2.School
[1] 0

$Child.2.Feeder.School
[1] 0

$Last.Contact.By.Anyone
[1] 0

$Lifetime.Giving
[1] 0

$HH.Lifetime.Giving
[1] 0

$Total.Giving.Years
[1] 0

$Total.Giving.Fiscal.Years
[1] 0

$Max.Consec.Fiscal.Years
[1] 0

$HH.Life.Hard.Credit
[1] 0

$HH.Life.Soft.Credit
[1] 0

$Contact.Rules
[1] 0

$Spouse.Mail.Rules
[1] 0

$HH.First.Gift.Amount
[1] 0

$HH.First.Gift.Date
[1] 0

$HH.First.Gift.Fund
[1] 0

$LegacyLeader..compass.score.
[1] 0

$Months.Since.Last.Gift
[1] 0

$Donor.Segment
[1] 0

$Compass.Score
[1] 0

$Scholarship
[1] 0

$Age
[1] 0

$Spouse.Age
[1] 0

$Assignment_flag
[1] 0

$No_of_Children
[1] 0

$nmb_degree
[1] 0

$Conferral.Date.1.Age
[1] 0

$Conferral.Date.2.Age
[1] 0

$Last.Contact.Age
[1] 0

$HH.First.Gift.Age
[1] 0

$major_gifter
[1] 2

$age_range
[1] 0

$region
[1] 0

$zipcode_slry
[1] 0

$zipslry_range
[1] 0

$sex_fct
[1] 5

$sex_simple
[1] 4

$married_fct
[1] 2

$donorseg_fct
[1] 6

$donorseg_simple
[1] 5

$contact_fct
[1] 166

$contact_simple
[1] 5

$spomail_fct
[1] 111

$spomail_simple
[1] 5

$jobtitle_fct
[1] 12438

$jobtitle_simple
[1] 6

$deg1_fct
[1] 53

$deg1_simple
[1] 6

$deg2_fct
[1] 43

$deg2_simple
[1] 6

$maj1_fct
[1] 1276

$maj1_simple
[1] 6

$maj2_fct
[1] 364

$maj2_simple
[1] 6

$min1_fct
[1] 930

$min1_simple
[1] 6

$min2_fct
[1] 52

$min2_simple
[1] 7

$school1_fct
[1] 17

$school1_simple
[1] 6

$school2_fct
[1] 17

$school2_simple
[1] 6

$insttype_fct
[1] 3293

$insttype_simple
[1] 6

$extra_fct
[1] 8591

$extra_simple
[1] 6

$hhfirstgift_fct
[1] 818

$hhfirstgift_simple
[1] 6

$ch1_enroll_fct
[1] 8

$ch1_enroll_simple
[1] 5

$ch1_maj_fct
[1] 1309

$ch1_maj_simple
[1] 5

$ch1_min_fct
[1] 848

$ch1_min_simple
[1] 5

$ch1_school_fct
[1] 21

$ch1_school_simple
[1] 5

$ch1_feeder_fct
[1] 3013

$ch1_feeder_simple
[1] 5

$ch2_enroll_simple
[1] 5

$ch2_maj_fct
[1] 283

$ch2_maj_simple
[1] 5

$ch2_min_fct
[1] 146

$ch2_min_simple
[1] 5

$ch2_school_fct
[1] 19

$ch2_school_simple
[1] 5

$ch2_feeder_fct
[1] 331

$ch2_feeder_simple
[1] 5

$Married_simple
[1] 0

$hh.lifetime.giving_fct
[1] 7067

$HH.Lifetime.Giving.Plus
[1] 0

LASSO


#Using cv.glmnet from class
#ls(data_train) 
#is.factor(data_train$major_gifter)
#glimpse(data_train$Lifetime.Giving)

#data_train %>% 
#  select_if(is.factor) %>% 
#  glimpse()



library(glmnet)
library(glmnetUtils)
lasso_fit <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       #Alpha 1 for lasso
                       alpha = 1)


print(lasso_fit$lambda.min)
#
print(lasso_fit$lambda.1se)

plot(lasso_fit)

coef(lasso_fit)
#Default setting is lambda.1se

#From the book - showing convergence with lambda values
plot(lasso_fit$glmnet.fit, xvar="lambda")
#abline(v=log(c(lasso_fit$lambda.min, lasso_fit$lambda.1se)), lty=2)

enet_mod <- cva.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       alpha = seq(0,1, by = 0.1))

print(enet_mod)
plot(enet_mod)

ELASTICNET


minlossplot(enet_mod, 
            cv.type = "min")

get_alpha <- function(fit) {
  alpha <- fit$alpha
  error <- sapply(fit$modlist, 
                  function(mod) {min(mod$cvm)})
  alpha[which.min(error)]
}

get_model_params <- function(fit) {
  alpha <- fit$alpha
  lambdaMin <- sapply(fit$modlist, `[[`, "lambda.min")
  lambdaSE <- sapply(fit$modlist, `[[`, "lambda.1se")
  error <- sapply(fit$modlist, function(mod) {min(mod$cvm)})
  best <- which.min(error)
  data.frame(alpha = alpha[best], lambdaMin = lambdaMin[best],
             lambdaSE = lambdaSE[best], eror = error[best])
}

best_alpha <- get_alpha(enet_mod)
print(best_alpha)
get_model_params(enet_mod)

best_mod <- enet_mod$modlist[[which(enet_mod$alpha == best_alpha)]]

print(best_mod)

minlossplot(enet_mod, cv.type = "min")

Ridges plot - could be useful for plotting donations vs donor segment


library('ggridges')

summary(data_train$variable)

ggplot(data_train, aes(x = HH.Lifetime.Giving, y = region)) + geom_density_ridges(rel_min_height = 0.005) + xlim(c(25000, 100000)) + 
      ggtitle("HH Lifetime Giving by Region")

library('corrplot')

#removing ID zip and nonnumeric 
corrplot_data <- dataclean[-c(1:48,52:56,58:60,63,66:67,70:72,74:81,83:132)]

#Convert from character to numeric data type
convert_fac2num <- function(x){
  as.numeric(as.factor(x))
}

corrplot_data <- mutate_at(corrplot_data,
                     .vars = c(1:12),
                     .funs = convert_fac2num)
#making a matrix
cd_cor <- cor(corrplot_data)

#creating correlation
col <- colorRampPalette(c("#BB4400", "#EE9990", 
  "#FFFFFF", "#77AAEE", "#4477BB"))
corrplot(cd_cor, method="color", col=col(100),
  type="lower", addCoef.col = "black",
  tl.pos="lt", tl.col="black", 
  tl.cex=0.7, tl.srt=45, 
  number.cex=0.7,
  diag=FALSE)

#correlation matrix
# pairs(~Age + Months.Since.Last.Gift + donorseg_fct + 
#     nmb_degree + No_of_Children + HH.First.Gift.Age + HH.First.Gift.Amount + Total.Giving.Years,
#     col = corrplot_data$HH.Lifetime.Giving,
#     data = corrplot_data, 
#     main = "Donor Scatter Plot Matrix")

#worthless.. 

ggplot(data = corrplot_data, aes(x = nmb_degree, y = HH.Lifetime.Giving)) + 
  geom_point(aplha = 1/10)+
  geom_smooth(method = "lm", color ="red") 

Random Forest


library('randomForest')

rf_fit_donor <- randomForest(Lifetime.Giving ~ ., 
                       data = data_train,
                       type = classification,
                       mtry = 7,
                       na.action = na.roughfix,
                       ntree = 200,
                       importance=TRUE
                       )

print(rf_fit_donor)

varImpPlot(rf_fit_donor, sort = TRUE, 
           n.var = 5,
           type = 2, class = NULL, scale = TRUE, 
           main = deparse(substitute(rf_fit_donor)))

library('randomForestExplainer')

plot_min_depth_distribution(
  rf_fit_donor,
  k = 10,
  min_no_of_trees = 0,
  mean_sample = "top_trees",
  mean_scale = FALSE,
  mean_round = 2,
  main = "Distribution of minimal depth and its mean"
)
#Splitting Category out to check if the category is useful for analysis
data_category_split_out <- dataclean %>%
  mutate(Category.Codes = trim(strsplit(as.character(Category.Codes), "|", fixed = TRUE))) %>%
  unnest(Category.Codes) %>% pivot_wider(names_from = Category.Codes,values_from =Category.Codes, values_fn = length)
LS0tCnRpdGxlOiAiQlJPQ09ERSBTdW1tYXJ5IFN0YXRpc3RpY3MiCmF1dGhvcjogIkFhcm9uIFdpbGxpcywgQ2Fubm9uIEJyb29rZSwgSm9zaHVhIEhlbmRlcnNvbiwgUnlhbiBSYWRjbGlmZiIKc3VidGl0bGU6ICJCVVM2OTYgRmluYWwgUHJvamVjdCB2MTQuMzMzIChyZXBlYXRpbmcgb2YgY291cnNlKSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgojIFBsZWFzZSBsZWF2ZSB0aGlzIGNvZGUgY2h1bmsgYXMgaXMuIEl0IG1ha2VzIHNvbWUgc2xpZ2h0IGZvcm1hdHRpbmcgY2hhbmdlcyB0byBhbHRlciB0aGUgb3V0cHV0IHRvIGJlIG1vcmUgYWVzdGhldGljYWxseSBwbGVhc2luZy4gCgpsaWJyYXJ5KCdrbml0cicpCgoKIyBDaGFuZ2UgdGhlIG51bWJlciBpbiBzZXQgc2VlZCB0byB5b3VyIG93biBmYXZvcml0ZSBudW1iZXIKc2V0LnNlZWQoMTgxOCkKb3B0aW9ucyh3aWR0aD03MCkKb3B0aW9ucyhzY2lwZW49OTkpCgoKIyB0aGlzIHNldHMgdGV4dCBvdXRwdXR0ZWQgaW4gY29kZSBjaHVua3MgdG8gc21hbGwKb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGgud3JhcD01MCksdGlkeT1UUlVFLCBzaXplID0gInZzbWFsbCIpICAKb3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICMgImNhY2hpbmciIHN0b3JlcyBvYmplY3RzIGluIGNvZGUgY2h1bmtzIGFuZCBvbmx5IHJld3JpdGVzIGlmIHlvdSBjaGFuZ2UgdGhpbmdzCiAgICAgICAgICAgICAgIGNhY2hlID0gVFJVRSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICMgYXV0b21hdGljYWxseSBkb3dubG9hZHMgZGVwZW5kZW5jeSBmaWxlcwogICAgICAgICAgICAgICBhdXRvZGVwID0gVFJVRSwKICAgICAgICAgICAgICAgIyAKICAgICAgICAgICAgICAgY2FjaGUuY29tbWVudHMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIyAKICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLAogICAgICAgICAgICAgICAjIGNoYW5nZSBmaWcud2lkdGggYW5kIGZpZy5oZWlnaHQgdG8gY2hhbmdlIHRoZSBjb2RlIGhlaWdodCBhbmQgd2lkdGggYnkgZGVmYXVsdAogICAgICAgICAgICAgICBmaWcud2lkdGggPSA1LjUsICAKICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDQuNSwKICAgICAgICAgICAgICAgZmlnLmFsaWduPSdjZW50ZXInKQoKCmBgYAoKYGBge3Igc2V0dXAtMn0KCiMgQWx3YXlzIHByaW50IHRoaXMgb3V0IGJlZm9yZSB5b3VyIGFzc2lnbm1lbnQKc2Vzc2lvbkluZm8oKQpnZXR3ZCgpCgpgYGAKCgo8IS0tICMjIyBzdGFydCBhbnN3ZXJpbmcgeW91ciBwcm9ibGVtIHNldCBoZXJlIC0tPgo8IS0tIFlvdSBtYXkgZXhwb3J0IHlvdXIgaG9tZXdvcmsgaW4gZWl0aGVyIGh0bWwgb3IgcGRmLCB3aXRoIHRoZSBmb3JtZXIgdXN1YWxseSBiZWluZyBlYXNpZXIuIAogICAgIFRvIGV4cG9ydCBvciBjb21waWxlIHlvdXIgUm1kIGZpbGU6IGNsaWNrIGFib3ZlIG9uICdLbml0JyB0aGVuICdLbml0IHRvIEhUTUwnIC0tPgo8IS0tIEJlIHN1cmUgdG8gc3VibWl0IGJvdGggeW91ciAuUm1kIGZpbGUgYW5kIHRoZSBjb21waWxlZCAuaHRtbCBvciAucGRmIGZpbGUgZm9yIGZ1bGwgY3JlZGl0IC0tPgoKCmBgYHtyIHNldHVwLTN9CgojIGxvYWQgYWxsIHlvdXIgbGlicmFyaWVzIGluIHRoaXMgY2h1bmsgCmxpYnJhcnkoJ3RpZHl2ZXJzZScpCmxpYnJhcnkoImZzIikKbGlicmFyeSgnaGVyZScpCmxpYnJhcnkoJ2RwbHlyJykKbGlicmFyeSgndGlkeXZlcnNlJykKbGlicmFyeSgnZ2dwbG90MicpCmxpYnJhcnkoJ2dncmVwZWwnKQpsaWJyYXJ5KCdnZ3RoZW1lcycpCmxpYnJhcnkoJ2ZvcmNhdHMnKQpsaWJyYXJ5KCdyc2FtcGxlJykKbGlicmFyeSgnbHVicmlkYXRlJykKbGlicmFyeSgnZ2d0aGVtZXMnKQpsaWJyYXJ5KCdrYWJsZUV4dHJhJykKbGlicmFyeSgncGFzdGVjcycpCmxpYnJhcnkoJ3ZpcmlkaXMnKQpsaWJyYXJ5KCdwbG90bHknKQpsaWJyYXJ5KCd0aWR5cXVhbnQnKQpsaWJyYXJ5KCdzY2FsZXMnKQpsaWJyYXJ5KCJnZGF0YSIpCgojIG5vdGUsIGRvIG5vdCBydW4gaW5zdGFsbC5wYWNrYWdlcygpIGluc2lkZSBhIGNvZGUgY2h1bmsuIGluc3RhbGwgdGhlbSBpbiB0aGUgY29uc29sZSBvdXRzaWRlIG9mIGEgY29kZSBjaHVuay4gCgpgYGAKCgoKIyMgUGFydCAxIC0gRmluYWwgUHJvamVjdCBDbGVhbmluZyBhbmQgU3VtbWFyeSBTdGF0aXN0aWNzIAoKMWEpIExvYWRpbmcgZGF0YQoKYGBge3J9CgojUmVhZGluZyB0aGUgZGF0YSBpbiBhbmQgZG9pbmcgbWlub3IgaW5pdGlhbCBjbGVhbmluZyBpbiB0aGUgZnVuY3Rpb24gY2FsbAojUmVwcm9kdWNpYmxlIGRhdGEgYW5hbHlzaXMgc2hvdWxkIGF2b2lkIGFsbCBhdXRvbWF0aWMgc3RyaW5nIHRvIGZhY3RvciBjb252ZXJzaW9ucy4KI3N0cmlwLndoaXRlIHJlbW92ZXMgd2hpdGUgc3BhY2UgCiNuYS5zdHJpbmdzIGlzIGEgc3Vic3RpdHV0aW9uIHNvIGFsbCB0aGF0IGhhdmUgIiIgd2lsbCA9IG5hCmRhdGEgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZmluYWxfcHJvamVjdCIsICJkb25vcl9kYXRhLmNzdiIpLAogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBzdHJpcC53aGl0ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQoKCgpgYGAKCgoxYikgRml4aW5nIHRoZSB3b25reSBET0IgJiBEYXRhIGNsZWFudXAKCmBgYHtyfQoKIyhCaXJ0aGRhdGUgYW5kIEFnZSwgSUQgYXMgYSBudW1iZXIpYWRkaW5nIERPQiAoQWdlL1Nwb3VzZSBBZ2UpIGluIHllYXJzIGNvbHVtbnMgYW5kIGFkZGluZyB0d28gZmllbGRzIGZvciBhc3NpZ25tZW50IGFuZCBudW1iZXIgb2YgY2hpbGRyZW4gYW5kIG51bWJlciBvZiBkZWdyZWVzCmRhdGFjbGVhbiA8LSBkYXRhICU+JQogIG11dGF0ZShCaXJ0aGRhdGUgPSBpZmVsc2UoQmlydGhkYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIEJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShCaXJ0aGRhdGUgPSBtZHkoQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKEFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IEJpcnRoZGF0ZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICBtdXRhdGUoU3BvdXNlLkJpcnRoZGF0ZSA9IGlmZWxzZShTcG91c2UuQmlydGhkYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIFNwb3VzZS5CaXJ0aGRhdGUpKSAlPiUKICBtdXRhdGUoU3BvdXNlLkJpcnRoZGF0ZSA9IG1keShTcG91c2UuQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBTcG91c2UuQmlydGhkYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQogIG11dGF0ZShJRCA9IGFzLm51bWVyaWMoSUQpKSAlPiUgCiAgbXV0YXRlKEFzc2lnbm1lbnRfZmxhZyA9IGlmZWxzZShpcy5uYShBc3NpZ25tZW50Lk51bWJlciksIDAsMSkpICU+JSAKICBtdXRhdGUoIE5vX29mX0NoaWxkcmVuID0gaWZlbHNlKGlzLm5hKENoaWxkLjEuSUQpLDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoQ2hpbGQuMi5JRCksMSwyKSkpJT4lCiBtdXRhdGUoSUQgPSBhcy5udW1lcmljKElEKSkgJT4lIAogICAgbXV0YXRlKCBubWJfZGVncmVlID0gaWZlbHNlKGlzLm5hKERlZ3JlZS5UeXBlLjEpLDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoRGVncmVlLlR5cGUuMiksMSwyKSkpICU+JQojY29uZmVycmFsIGRhdGVzCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4xKSkgJT4lCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBtZHkoQ29uZmVycmFsLkRhdGUuMSkpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xLkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IENvbmZlcnJhbC5EYXRlLjEsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMiA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4yKSkgJT4lCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBtZHkoQ29uZmVycmFsLkRhdGUuMikpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yLkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IENvbmZlcnJhbC5EYXRlLjIsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiAgbXV0YXRlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPSBpZmVsc2UoTGFzdC5Db250YWN0LkJ5LkFueW9uZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBMYXN0LkNvbnRhY3QuQnkuQW55b25lKSkgJT4lCiAgbXV0YXRlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPSBtZHkoTGFzdC5Db250YWN0LkJ5LkFueW9uZSkpICU+JQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gTGFzdC5Db250YWN0LkJ5LkFueW9uZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICAKIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBpZmVsc2UoSEguRmlyc3QuR2lmdC5EYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIEhILkZpcnN0LkdpZnQuRGF0ZSkpICU+JQogIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBtZHkoSEguRmlyc3QuR2lmdC5EYXRlKSkgJT4lCm11dGF0ZShISC5GaXJzdC5HaWZ0LkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IEhILkZpcnN0LkdpZnQuRGF0ZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUgCiNtYWpvciBnaWZ0IAogIG11dGF0ZShtYWpvcl9naWZ0ZXIgPSBpZmVsc2UoTGlmZXRpbWUuR2l2aW5nID4gNTAwMDAsIDEsMCkgJT4lIGZhY3RvciguLCBsZXZlbHMgPSBjKCIwIiwiMSIpKSkgJT4lCiNzcGxpdHRpbmcgdXAgdGhlIGFnZSBpbnRvIHJhbmdlcyBhbmQgY3JlYXRpbmcgY2F0ZWdvcnkgZm9yIGVhc3kgdmlzdWFsaXphdGlvbiAKICBtdXRhdGUoYWdlX3JhbmdlID0gCiAgICBpZmVsc2UoQWdlICVpbiUgMTA6MTksICIxMCA8IDIwIHllYXJzIG9sZCIsCiAgICBpZmVsc2UoQWdlICVpbiUgMjA6MjksICIyMCA8IDMwIHllYXJzIG9sZCIsIAogICAgaWZlbHNlKEFnZSAlaW4lIDMwOjM5LCAiMzAgPCA0MCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDQwOjQ5LCAiNDAgPCA1MCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDUwOjU5LCAiNTAgPCA2MCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDYwOjY5LCAiNjAgPCA3MCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDcwOjc5LCAiNzAgPCA4MCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDgwOjg5LCAiODAgPCA5MCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDkwOjEyMCwgIjkwKyB5ZWFycyBvbGQiLAogICAgTkEpKSkpKSkpKSkpICU+JQojY3JlYXRpbmcgYSByZWdpb24gY29sdW1uIHVzaW5nIHRoZSBjb3VudHkgZGF0YSBhbmQgdGhlIE9NQiBNU0EgKE1ldHJvcG9saXRhbiBTdGF0aXN0aWNhbCBBcmVhKSBkZWZpbml0aW9ucwogIG11dGF0ZShyZWdpb24gPSAKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBMdWlzIE9iaXNwbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIktlcm4iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gQmVybmFyZGlubyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbnRhIEJhcmJhcmEiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJWZW50dXJhIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTG9zIEFuZ2VsZXMiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJPcmFuZ2UiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJSaXZlcnNpZGUiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gRGllZ28iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJJbXBlcmlhbCIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIktpbmciICYgU3RhdGUgPT0gIldBIiwgIlNlYXR0bGUiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU25vaG9taXNoIiAmIFN0YXRlID09ICJXQSIsICJTZWF0dGxlIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlBpZXJjZSIgJiBTdGF0ZSA9PSAiV0EiLCAiU2VhdHRsZSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDbGFja2FtYXMiICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNvbHVtYmlhIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNdWx0bm9tYWgiICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIldhc2hpbmd0b24iICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIllhbWhpbGwiICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNsYXJrIiAmIFN0YXRlID09ICJXQSIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTa2FtYW5pYSIgJiBTdGF0ZSA9PSAiV0EiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiRGVudmVyIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQXJhcGFob2UiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJKZWZmZXJzb24iICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJBZGFtcyIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkRvdWdsYXMiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJCcm9vbWZpZWxkIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLCAgICAKICAgIGlmZWxzZShDb3VudHkgPT0gIkVsYmVydCIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlBhcmsiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDbGVhciBDcmVlayIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkFsYW1lZGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNvbnRyYSBDb3N0YSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTWFyaW4iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1vbnRlcmV5IiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJOYXBhIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gQmVuaXRvIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gRnJhbmNpc2NvIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gTWF0ZW8iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbnRhIENsYXJhIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBDcnV6IiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTb2xhbm8iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNvbm9tYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgICAgICAgIE5BKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpICU+JQogIG11dGF0ZShyZWdpb24gPSAKICAgIGlmZWxzZShDb3VudHkgPT0gIktpbmdzIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJRdWVlbnMiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk5ldyBZb3JrIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJCcm9ueCIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUmljaG1vbmQiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIldlc3RjaGVzdGVyIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJCZXJnZW4iICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkh1ZHNvbiIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUGFzc2FpYyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUHV0bmFtIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJSb2NrbGFuZCIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU3VmZm9sayIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTmFzc2F1IiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNaWRkbGVzZXgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1vbm1vdXRoIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJPY2VhbiIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU29tZXJzZXQiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkVzc2V4IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJVbmlvbiIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTW9ycmlzIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTdXNzZXgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkh1bnRlcmRvbiIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUGlrZSIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgcmVnaW9uKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpICU+JQoKIyBjb2RlIG5vciBjYWwgcmVnaW9uIGFzIGFsbCBvdGhlcnMgaW4gQ0Egbm90IGFscmVhZHkgZGVmaW5lZAogIG11dGF0ZShyZWdpb24gPSAKICAgIGlmZWxzZShTdGF0ZSA9PSAiQ0EiICYgaXMubmEocmVnaW9uKSA9PSBUUlVFLCAiTm9yIENhbCIsIHJlZ2lvbikpCgojSW5pdGlhbCBSZW1vdmFsIG9mIENvbHVtbnMgdGhhdCBwcm92aWRlIG5vIGJlbmVmaXQgCgpkYXRhY2xlYW4gPC0gc3Vic2V0KGRhdGFjbGVhbixzZWxlY3QgPSAtYyhBc3NpZ25tZW50Lk51bWJlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuaGFzLkhpc3RvcmljYWwuTW5ncgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN1ZmZpeAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuUm9sZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuVGl0bGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlN0YXR1cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN0cmF0ZWd5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvZ3Jlc3MuTGV2ZWwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lkdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5DYXRlZ29yeQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEZ1bmRpbmcuTWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEV4cGVjdGVkLkJvb2suRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxRdWFsaWZpY2F0aW9uLkFtb3VudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkFtb3VudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGFyZC5HaWZ0LlRvdGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFNvZnQuQ3JlZGl0LlRvdGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFRvdGFsLkFzc2lnbm1lbnQuR2lmdHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTm8ub2YuUGxlZGdlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFByb3Bvc2FsLk5vdGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhILkxpZmUuU3BvdXNlLkNyZWRpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxMYXN0LkNvbnRhY3QuQnkuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxYLi5vZi5Db250YWN0cy5CeS5NYW5hZ2VyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLERvbm9yU2VhcmNoLlJhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLGlXYXZlLlJhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFdlYWx0aEVuZ2luZS5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQaGlsYW50aHJvcGljLkNvbW1pdG1lbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKI2NsZWFuaW5nIHVwIHppcCBjb2RlcyByZW1vdmluZyAtNCBhZnRlciAKZGF0YWNsZWFuJFppcCA8LSBnc3ViKGRhdGFjbGVhbiRaaXAsIHBhdHRlcm49Ii0uKiIsIHJlcGxhY2VtZW50ID0gIiIpCgojYWRkaW5nIHppcCBjb2RlIGRhdGEgYW5kIGNvbHVtbiAKemlwIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiU2FsYXJ5X1ppcGNvZGUuY3N2IiksCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpCgoKI2FkZGluZyB6aXAgc2FsYXJ5IGNvbHVtbgpkYXRhY2xlYW4gPC1kYXRhY2xlYW4gJT4lCiAgICBtdXRhdGUoemlwY29kZV9zbHJ5ID0gVkxPT0tVUChaaXAsIHppcCwgTkFNRSwgUzE5MDJfQzAzXzAwMkUpKSAlPiUKI3NscnkgcmFuZ2UgCiAgbXV0YXRlKHppcHNscnlfcmFuZ2UgPSAKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxMDAwMDo4OTk5OSwgIjkwSy05OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDkwMDAwOjk5OTk5LCAiOTBLLTk5SyIsCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDAwOjE0OTk5OSwgIjEwMEstMTQ5SyIsIAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDE1MDAwMDoxOTk5OTksICIxNTBLLTE5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDIwMDAwMDoyNDk5OTksICIyMDBLLTI0OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDI1MDAwMDoyOTk5OTksICIyNTBLLTI5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDMwMDAwMDozNDk5OTksICIzMDBLLTM0OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDM1MDAwMDozOTk5OTksICIzNTBLLTM5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDQwMDAwMDo0OTk5OTksICI0MDBLLTQ5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDUwMDAwMDo5OTk5OTksICI1MDBLLTk5OUsiLAogICAgTkEpKSkpKSkpKSkpKQoKc3VtKGlzLm5hKGRhdGFjbGVhbiR6aXBjb2RlX3NscnkpKQoKI2FkZGluZyBzY2hvbGFyc2hpcCBkYXRhICh5L24pCnNjaGxyIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAic2Nob2xhcnNoaXAuY3N2IiksCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpCgojYWRkaW5nIHNjaG9sYXJzaGlwIGNvbHVtbgpkYXRhY2xlYW4gPC1kYXRhY2xlYW4gJT4lCiAgICBtdXRhdGUoc2Nob2xhcnNoaXAgPSBWTE9PS1VQKElELCBzY2hsciwgSUQsIFNDSE9MQVJTSElQKSkgCgojcmVwbGFjaW5nIE5BIHdpdGggMCAKIGRhdGFjbGVhbiRzY2hvbGFyc2hpcCA8LSByZXBsYWNlX25hKGRhdGFjbGVhbiRzY2hvbGFyc2hpcCwnMCcpCiAKI3JlcGxhY2luZyBZIHdpdGggMSAKZGF0YWNsZWFuJHNjaG9sYXJzaGlwPC1pZmVsc2UoZGF0YWNsZWFuJHNjaG9sYXJzaGlwPT0iWSIsMSwwKQoKI2NoZWNraW5nIGhvdyBtYW55IGFyZSBOCnRhYmxlKGRhdGFjbGVhbiRzY2hvbGFyc2hpcCkKCgojY2hlY2tpbmcgYW5kIGRlbGV0aW5nIHNjaG9sYXJzaGlwIGNvbHVtbiAKY2xhc3MoZGF0YWNsZWFuJHNjaGxyX2ZjdCkKZGF0YWNsZWFuID0gc3Vic2V0KGRhdGFjbGVhbiwgc2VsZWN0ID0gLWMoc2Nob2xhcnNoaXApKQogIAojY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZXMgTiA+MSBpbmRpY2F0ZXMgYSByZWNvcmRzIHZhbHVlcyBhcmUgaW4gdGhlIGZpbGUgdHdpY2UgCmRhdGFjbGVhbiAlPiUgZ3JvdXBfYnkoSUQpICU+JSBjb3VudCgpICU+JSBhcnJhbmdlKGRlc2MobikpCgojcmVtb3ZpbmcgZHVwbGljYXRlZCByZWNvcmRzCmRhdGFjbGVhbiA8LSB1bmlxdWUoZGF0YWNsZWFuKQoKI1ZlcmlmeWluZyBuID0gMSBubyBJRCB3aXRoIG11bHRpcGxlIHJlY29yZHMgY2xlYW5lZCBvZiBkdXBlcwpkYXRhY2xlYW4gJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQoKYGBgCgoKMWQgQ3JlYXRpbmcgbWFueSBtYW55IGZhY3RvciB2YXJpYWJsZXMKCmBgYHtyfQoKZGF0YWNsZWFuIDwtIAogIGRhdGFjbGVhbiAlPiUgCiAgI1NFWAogIG11dGF0ZShzZXhfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNleCksCnNleF9zaW1wbGUgPSAKICAgIGZjdF9sdW1wX24oU2V4LCBuID0gNCksCiNNQVJSSUVECm1hcnJpZWRfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1hcnJpZWQpLAogICNET05PUiBTRUdNRU5UCiAgZG9ub3JzZWdfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERvbm9yLlNlZ21lbnQpLAogICAgICAgICBkb25vcnNlZ19zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKERvbm9yLlNlZ21lbnQsIG4gPSA0KSwKICAjQ09OVEFDVCBSVUxFCiAgICAgICAgIGNvbnRhY3RfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENvbnRhY3QuUnVsZXMpLAogICAgICAgICBjb250YWN0X3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ29udGFjdC5SdWxlcywgbiA9IDQpLAogICNTUE9VU0UgTUFJTAogICAgICAgICBzcG9tYWlsX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTcG91c2UuTWFpbC5SdWxlcyksCiAgICAgICAgIHNwb21haWxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTcG91c2UuTWFpbC5SdWxlcywgbiA9IDQpLAogICNKT0IgVElUTEUKICAgICAgICAgam9idGl0bGVfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEpvYi5UaXRsZSksCiAgICAgICAgIGpvYnRpdGxlX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oSm9iLlRpdGxlLCBuID0gNSksCiAgI0RFR1JFRSBUWVBFIDEKICAgICAgICAgZGVnMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMSksCiAgICAgICAgIGRlZzFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4xLCBuID0gNSksCiAgI0RFR1JFRSBUWVBFIDIKICAgICAgICAgZGVnMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMiksCiAgICAgICAgIGRlZzJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4yLCBuID0gNSksCiAgI01BSk9SIDEKICAgICAgICAgbWFqMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFqb3IuMSksCiAgICAgICAgIG1hajFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4xLCBuID0gNSksCiAgI01BSk9SIDIKICAgICAgICAgbWFqMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFqb3IuMiksCiAgICAgICAgIG1hajJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4yLCBuID0gNSksCiAgI01JTk9SIDEKICAgICAgICAgbWluMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMSksCiAgICAgICAgIG1pbjFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNaW5vci4xLCBuID0gNSksCiAgI01JTk9SIDIKICAgICAgICAgbWluMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMiksCiAgICAgICAgIG1pbjJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNaW5vci4yLCBuID0gNSksCiAgI1NDSE9PTCAxCiAgICAgICAgIHNjaG9vbDFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4xKSwKICAgICAgICAgc2Nob29sMV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4xLCBuID0gNSksCiAgI1NDSE9PTCAyCiAgICAgICAgIHNjaG9vbDJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4yKSwKICAgICAgICAgc2Nob29sMl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4yLCBuID0gNSksCiAgI0lOU1RJVFVUSU9OIFRZUEUKICAgICAgICAgaW5zdHR5cGVfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEluc3RpdHV0aW9uLlR5cGUpLAogICAgICAgICBpbnN0dHlwZV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEluc3RpdHV0aW9uLlR5cGUsIG4gPSA1KSwKICAjRVhUUkFDVVJSSUNVTEFSCiAgICAgICAgIGV4dHJhX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShFeHRyYWN1cnJpY3VsYXIpLAogICAgICAgICBleHRyYV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEV4dHJhY3VycmljdWxhciwgbiA9IDUpLAogICNISCBGSVJTVCBHSUZUIEZVTkQKICAgICAgICAgaGhmaXJzdGdpZnRfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEhILkZpcnN0LkdpZnQuRnVuZCksCiAgICAgICAgIGhoZmlyc3RnaWZ0X3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oSEguRmlyc3QuR2lmdC5GdW5kLCBuID0gNSksCiNDSElMRCAxIEVOUk9MTCBTVEFUVVMKICAgICAgICAgY2gxX2Vucm9sbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5FbnJvbGwuU3RhdHVzKSwKICAgICAgICAgY2gxX2Vucm9sbF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRW5yb2xsLlN0YXR1cywgbiA9IDQpLAojQ0hJTEQgMSBNQUpPUgogICAgICAgICBjaDFfbWFqX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLk1ham9yKSwKICAgICAgICAgY2gxX21hal9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuTWFqb3IsIG4gPSA0KSwKI0NISUxEIDEgTUlOT1IKICAgICAgICAgY2gxX21pbl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NaW5vciksCiAgICAgICAgIGNoMV9taW5fc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLk1pbm9yLCBuID0gNCksCiNDSElMRCAxIFNDSE9PTAogICAgICAgICBjaDFfc2Nob29sX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLlNjaG9vbCksCiAgICAgICAgIGNoMV9zY2hvb2xfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLlNjaG9vbCwgbiA9IDQpLAojQ0hJTEQgMSBGRUVERVIKICAgICAgICAgY2gxX2ZlZWRlcl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5GZWVkZXIuU2Nob29sKSwKICAgICAgICAgY2gxX2ZlZWRlcl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRmVlZGVyLlNjaG9vbCwgbiA9IDQpLAojQ0hJTEQgMiBFTlJPTEwgU1RBVFVTCiAgICAgICAgIGNoMV9lbnJvbGxfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRW5yb2xsLlN0YXR1cyksCiAgICAgICAgIGNoMl9lbnJvbGxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLkVucm9sbC5TdGF0dXMsIG4gPSA0KSwKI0NISUxEIDIgTUFKT1IKICAgICAgICAgY2gyX21hal9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5NYWpvciksCiAgICAgICAgIGNoMl9tYWpfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLk1ham9yLCBuID0gNCksCiNDSElMRCAyIE1JTk9SCiAgICAgICAgIGNoMl9taW5fZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuTWlub3IpLAogICAgICAgICBjaDJfbWluX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NaW5vciwgbiA9IDQpLAojQ0hJTEQgMiBTQ0hPT0wKICAgICAgICAgY2gyX3NjaG9vbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5TY2hvb2wpLAogICAgICAgICBjaDJfc2Nob29sX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDIgRkVFREVSCiAgICAgICAgIGNoMl9mZWVkZXJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRmVlZGVyLlNjaG9vbCksCiAgICAgICAgIGNoMl9mZWVkZXJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLkZlZWRlci5TY2hvb2wsIG4gPSA0KSwKICAgICkKCgoKI2NoZWNraW5nIHRvIHNlZSBpZiBpdHMgYSBmYWN0b3IKI2NsYXNzKGRhdGFjbGVhbiRzZXhfZmN0KQojY2xhc3MoZGF0YWNsZWFuJGRvbm9yc2VnX2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRjb250YWN0X2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRzcG9tYWlsX2ZjdCkKCiNjaGVja2luZyBsZXZlbHMKI2xldmVscyhkYXRhY2xlYW4kc2V4X3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kZG9ub3JzZWdfc2ltcGxlKQojbGV2ZWxzKGRhdGFjbGVhbiRjb250YWN0X3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kc3BvbWFpbF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJGhoZmlyc3RnaWZ0X3NpbXBsZSkKCiNjcmVhdGluZyBhIHRhYmxlIGFnYWluc3QgU2V4IGNvbHVtbiAKI3RhYmxlKGRhdGFjbGVhbiRzZXhfZmN0LCBkYXRhY2xlYW4kc2V4X3NpbXBsZSkKCgpgYGAKClJlZ2lvbiBBbmFseXNpcwoKYGBge3J9CiNncm91cGluZyBieSByZWdpb24gYW5kIGFuYWx5emluZyAKZGF0YWNsZWFuICU+JQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKHJlZ2lvbiksCiAgICAgICAgICAgIG1lYW5fdG90YWxfZ2l2ID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcpKSAlPiUKICBhcnJhbmdlKC1Db3VudCkgJT4lCiAgZmlsdGVyKENvdW50ID49IDEwMCkgJT4lCiAgbXV0YXRlKG1lYW5fdG90YWxfZ2l2ID0gZG9sbGFyKG1lYW5fdG90YWxfZ2l2KSkgJT4lCiAga2FibGUoY29sLm5hbWVzID0gYygiUmVnaW9uIiwgIkNvdW50IiwgIk1lYW4gSEggTGlmZXRpbWUgR2l2aW5nIiksIGFsaWduPXJlcCgnYycsIDMpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKCgpEb25vclNlZ21lbnQgQW5hbHlzaXMKCmBgYHtyfQojZ3JvdXBpbmcgYnkgZG9ub3JzZWdtZW50IGFuZCBhbmFseXppbmcgCmRhdGFjbGVhbiAlPiUKICBncm91cF9ieShEb25vci5TZWdtZW50KSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBsZW5ndGgoRG9ub3IuU2VnbWVudCksCiAgICAgICAgICAgIG1lYW5fdG90YWxfZ2l2ID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcpKSAlPiUKICBhcnJhbmdlKC1Db3VudCkgJT4lCiAgZmlsdGVyKENvdW50ID49IDEwMCkgJT4lCiAgI2FkZGVkIHNjYWxlcyBwYWNrYWdlIHRvIGhhdmUgdGhlIHZhbHVlcyBzaG93IGluIGRvbGxhciAKICBtdXRhdGUobWVhbl90b3RhbF9naXYgPSAoZG9sbGFyKG1lYW5fdG90YWxfZ2l2KSkpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yIFNlZ21lbnQiLCAiQ291bnQiLCAiTWVhbiBISCBMaWZldGltZSBHaXZpbmciKSwgYWxpZ249cmVwKCdjJywgMykpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYAoKRmlyc3QgZ2lmdCBzaXplIApgYGB7cn0KYXEgPC0gcXVhbnRpbGUoZGF0YWNsZWFuJEhILkZpcnN0LkdpZnQuQW1vdW50LCBwcm9icyA9IGMoLjI1LC41MCwuNzUsLjksLjk5KSwgbmEucm0gPSBUUlVFKQoKYXEgPC0gYXMuZGF0YS5mcmFtZShhcSkKCmFxJGFxIDwtIGRvbGxhcihhcSRhcSkKCmFxICU+JQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYApDb25zZWN1dGl2ZSBnaXZpbmcKYGBge3J9CiNjb25zZWN1dGl2ZSB5ZWFycyBvZiBnaXZpbmcgCmRhdGFjbGVhbiAlPiUKICBmaWx0ZXIoTWF4LkNvbnNlYy5GaXNjYWwuWWVhcnMgPiAwKSAlPiUKICBnZ3Bsb3QoYWVzKE1heC5Db25zZWMuRmlzY2FsLlllYXJzKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMwMDI4NDUiLCBiaW5zID0gMjApICsgCiAgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkNvbnNlY3V0aXZlIFllYXJzIG9mIEdpdmluZyBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMCwyKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAwMCw1MDAwKSkgCgoKCmBgYAoKTGlmZXRpbWUgZ2l2aW5nIGJhc2VkIG9uIG51bWJlciBvZiBjaGlsZHJlbiAKCmBgYHtyfQpkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKEhILkxpZmV0aW1lLkdpdmluZyA8PSAxMDAwMCkgJT4lCiAgZmlsdGVyKEhILkxpZmV0aW1lLkdpdmluZyA+IDApICU+JQogIG11dGF0ZShgTm9fb2ZfQ2hpbGRyZW5gID0gYXMuZmFjdG9yKGBOb19vZl9DaGlsZHJlbmApKSAlPiUKICBnZ3Bsb3QoYWVzKEhILkxpZmV0aW1lLkdpdmluZywgZmlsbCA9IGBOb19vZl9DaGlsZHJlbmApKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwLDEwMDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwMCw1MDAwKSkgKwogIGdndGl0bGUoIkdpdmluZyBkaXN0cmlidXRpb24gYW5kIG51bWJlciBvZiBjaGlsZHJlbiIpKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzAwMjg0NSIsICIjMDBjZmNjIiwgIiNmZjk5NzMiKSkKCgoKYGBgCgoKTWVhbiwgTWVkaWFuLCBhbmQgQ291bnQgb2YgR2l2aW5nIGluIEFnZSBSYW5nZXMgCgpgYGB7cn0KCmFnZV9yYW5nZV9naXZpbmcgPC0gZGF0YWNsZWFuICU+JQogIGdyb3VwX2J5KGFnZV9yYW5nZSkgJT4lCiAgc3VtbWFyaXNlKGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWVkX2dpdmluZyA9IG1lZGlhbihISC5MaWZldGltZS5HaXZpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGFtb3VudF9vZl9wZW9wbGVfaW5fYWdlX3JhbmdlID0gbigpKQoKCmdsaW1wc2UoYWdlX3JhbmdlX2dpdmluZykKCmBgYAoKCgoKCiMjIFBhcnQgMiAtIFN1bW1hcnkgU3RhdGlzdGljcwoKMmEpIFBsb3R0aW5nIGF2ZXJhZ2UgZ2l2aW5nIGJ5IGFnZSByYW5nZSAKCgpgYGB7cn0KCmFnZV9yYW5nZV9naXZpbmcgPC0KICBhZ2VfcmFuZ2VfZ2l2aW5nICU+JQogIG11dGF0ZShhZ2VfcmFuZ2UgPSBmYWN0b3IoYWdlX3JhbmdlKSkKCmdncGxvdChhZ2VfcmFuZ2VfZ2l2aW5nLCBhZXMoYWdlX3JhbmdlLCBhdmdfZ2l2aW5nKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTEpKSArIGxhYnMoeCA9ICJBZ2UgUmFuZ2UiLCB5ID0gIkF2ZXJhZ2UgR2l2aW5nIikgKwogICAgICBnZ3RpdGxlKCJBdmVyYWdlIEdpdmluZyBDb21wYXJlZCBBY3Jvc3MgQWdlIFJhbmdlcyIpCgoKYGBgCgoKMmIpIENvdW50IG9mIGRvbm9ycyBiYXNlZCBvbiBhZ2UgcmFuZ2UgKGFub3RoZXIgd2F5IHRvIGxvb2sgYXQgaXQpCgoKYGBge3J9CgpnZ3Bsb3QoZGF0YWNsZWFuLCAKICAgICAgIGFlcyhhZ2VfcmFuZ2UpKSArIAogICAgICAgZ2VvbV9iYXIoKSArIAogICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0xKSkgKyAKICBsYWJzKHRpdGxlID0gIkNvdW50IG9mIEFnZSBSYW5nZXMiLCB4ID0gIiIsIHkgPSAiIikKICAKCmBgYAoKMmMpIEJveHBsb3Qgb2YgdGhlIEFnZSBSYW5nZXMgQWdhaW5zdCB0aGUgTGlmZXRpbWUgR2l2aW5nIEFtb3VudHMgd2l0aCBhIGxvZyBzY2FsZSBhcHBsaWVkIC0gdGhlIHJlYXNvbiB3ZSBhcHBsaWVkIGxvZyBzY2FsZSBpcyB0byByZXNvbHZlIGlzc3VlcyB3aXRoIHZpc3VhbGl6YXRpb25zIHRoYXQgc2tldyB0b3dhcmRzIGxhcmdlIHZhbHVlcyBpbiBvdXIgZGF0YXNldC4gCgoKYGBge3J9CgpnZ3Bsb3QoZGF0YWNsZWFuLCBhZXMoYWdlX3JhbmdlLEhILkxpZmV0aW1lLkdpdmluZyxmaWxsID0gYWdlX3JhbmdlKSkgKyAKICBnZW9tX2JveHBsb3QoCiAgb3V0bGllci5jb2xvdXIgPSAicmVkIikgKyAKICBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSxoanVzdD0xKSkgICsgbGFicyh4ID0gIkFnZSBSYW5nZSIsIHkgPSAiTGlmZXRpbWUgR2l2aW5nIEFtb3VudCIpICsKICAgICAgZ2d0aXRsZSgiTGlmZXRpbWUgR2l2aW5nIENvbXBhcmVkIEFjcm9zcyBBZ2UgUmFuZ2VzIikKICAKCmBgYAoKMmQpIFNwbGl0dGluZyBieSBhZ2UgYW5kIGdlbmRlciAKCgpgYGB7cn0KCgojY3JlYXRpbmcgYm94cGxvdHMgCmRhdGFjbGVhbiAlPiUgCiAgZmlsdGVyKEFnZSA8IDEwMCkgJT4lICNyZW1vdmluZyB0aGUgd2VpcmQgb3V0bGllcnMgdGhhdCBhcmUgb3ZlciAxMDAgCiAgZmlsdGVyKFNleCAlaW4lIGMoIk0iLCAiRiIpKSAlPiUKICBnZ3Bsb3QoYWVzKFNleCwgQWdlKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIHRoZW1lX2Vjb25vbWlzdCgpICsgCiAgZ2d0aXRsZSgiQWdlcyBvZiBEb25vcnMgQmFzZWQgb24gR2VuZGVyIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKQogIAoKYGBgCgpHaXZpbmcgYnkgZ2VuZGVyCgpgYGB7cn0KCiNyZW1vdmUgTkFzIFUgWAoKZGF0YWNsZWFuMiA8LSBkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKFNleCAlaW4lIGMoIk0iLCAiRiIpKSAKCnEgPC0gZ2dwbG90KGRhdGFjbGVhbjIpIApxICsgc3RhdF9zdW1tYXJ5X2JpbigKICBhZXMoeSA9IEhILkxpZmV0aW1lLkdpdmluZywgeCA9IFNleCksIAogIGZ1bi55ID0gIm1lYW4iLCBnZW9tID0gImJhciIpIAogIApzdW1tYXJ5KGRhdGFjbGVhbiRzZXhfc2ltcGxlKQoKYGBgCgpNZWFuIGFnZSBieSBnZW5kZXIKCmBgYHtyfQojYnJlYWtkb3duIG9mIHNleHMgCnRhbGx5KGdyb3VwX2J5KGRhdGFjbGVhbiwgU2V4KSkKCnN1bW1hcml6ZShncm91cF9ieShkYXRhY2xlYW4sIFNleCksIAogICAgICAgICAgYXZnX2dpdmluZyA9IG1lYW4oSEguTGlmZXRpbWUuR2l2aW5nLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgYXZnX2FnZSA9IG1lYW4oQWdlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgbWVkX2FnZSA9IG1lZGlhbihBZ2UsIG5hLnJtID0gVFJVRSkpCgojZ3JvdXBpbmcgYnkgc2V4IGFuZCBhZ2UgcmFuZ2UgZm9yIHNsaWRlcyAKdGFsbHkoZ3JvdXBfYnkoZGF0YWNsZWFuLCBTZXgsIGFnZV9yYW5nZSkpCgoKCmBgYAoKCgoyZSkgRGlzdHJpYnV0aW9uIG9mIHBlb3BsZSBpbiB0aGUgc3RhdGVzIHRoYXQgdGhleSBsaXZlLgoKYGBge3J9CgogIGRhdGFjbGVhbiAlPiUKICBtdXRhdGUoU3RhdGUgPSBpZmVsc2UoU3RhdGUgPT0gIiAiLCAiTkEiLCBTdGF0ZSkpICU+JQogIGZpbHRlcihTdGF0ZSAhPSAiTkEiKSAlPiUKICBncm91cF9ieShTdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKFN0YXRlKSkgJT4lCiAgZmlsdGVyKENvdW50ID4gODAwKSAlPiUKICBhcnJhbmdlKC1Db3VudCkgJT4lCiAga2FibGUoY29sLm5hbWVzID0gYygiRG9ub3IncyBTdGF0ZSIsICJDb3VudCIpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygiY29uZGVuc2VkIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYAoKMmYpIExvb2tpbmcgYXQgYWxsIGRvbm9ycyBmaXJzdCBnaWZ0IGFtb3VudC4gNzUlIG1hZGUgYSBmaXJzdCBnaWZ0IG9mIDwxMDAuIAoKYGBge3J9Cgogbm9fbm9uX2Rvbm9ycyA8LSBkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKExpZmV0aW1lLkdpdmluZyAhPSAwKQogIApuZCA8LSBxdWFudGlsZShub19ub25fZG9ub3JzJEhILkZpcnN0LkdpZnQuQW1vdW50LCBwcm9icyA9IGMoLjI1LC41MCwuNzUsLjksLjk5KSwgbmEucm0gPSBUUlVFKQoKbmQgPC0gYXMuZGF0YS5mcmFtZShuZCkKCm5kICU+JQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKICAKCgpgYGAKCgoKIyMgUGFydCAzIC0gTW9kZWxpbmcKClNwbGl0IGRhdGEgYW5kIGNyZWF0ZSBhIG5ldyBzZXQgZm9yIGVhc2llciBhbmFseXNpcwoKYGBgIHtyfQoKI2NvbnZlcnRpbmcgbWFycmllZCBZIGFuZCBOIHRvIDEgYW5kIDAgCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgICAgIG11dGF0ZShNYXJyaWVkX3NpbXBsZSA9IGlmZWxzZShNYXJyaWVkID09ICJOIiwwLDEpKQoKCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKGhoLmxpZmV0aW1lLmdpdmluZ19mY3QgPSBhcy5mYWN0b3IoSEguTGlmZXRpbWUuR2l2aW5nKSkgJT4lCiAgbXV0YXRlKEhILkxpZmV0aW1lLkdpdmluZy5QbHVzID0gbG9nKEhILkxpZmV0aW1lLkdpdmluZyArIDEpKQoKCgojQ3JlYXRpbmcgdGhlIHZlbHZldCBzZXQgLSBvbmx5IHRoZSBiZXN0IGNhbiBlbnRlcgoKZGF0YXZlbHZldCA8LSBzdWJzZXQoZGF0YWNsZWFuLHNlbGVjdCA9IC1jKAogIENhdGVnb3J5LkNvZGVzLAogIENhdGVnb3J5LkRlc2NyaXB0aW9ucywKICBDb250YWN0LlJ1bGVzLAogIENvdW50eSwKICBEZWdyZWUuVHlwZS4xLAogIERlZ3JlZS5UeXBlLjIsCiAgRG9ub3IuU2VnbWVudCwKICBDb25mZXJyYWwuRGF0ZS4xLAogIENvbmZlcnJhbC5EYXRlLjIsCiAgQ29udGFjdC5SdWxlcywKICBEZWdyZWUuVHlwZS4xLAogIERlZ3JlZS5UeXBlLjIsCiAgRXh0cmFjdXJyaWN1bGFyLAogIEhILkZpcnN0LkdpZnQuRnVuZCwKICBJRCwKICBJbnN0aXR1dGlvbi5UeXBlLAogIEpvYi5UaXRsZSwKICBMYXN0LkNvbnRhY3QuQnkuQW55b25lLAogIE1ham9yLjEsCiAgTWFqb3IuMiwKICBTY2hvb2wuMSwKICBTY2hvb2wuMiwKICBTcG91c2UuQmlydGhkYXRlLAogIFNwb3VzZS5NYWlsLlJ1bGVzLAogIFppcAopKQoKCgpsaWJyYXJ5KCJyc2FtcGxlIikKCmRhdGFfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChkYXRhdmVsdmV0LCBwcm9wID0gMC43NSkKCmRhdGFfdHJhaW4gPC0gdHJhaW5pbmcoZGF0YV9zcGxpdCkKZGF0YV90ZXN0IDwtIHRlc3RpbmcoZGF0YV9zcGxpdCkKCmBgYAoKCgpgYGB7cn0KcCA8LSBkYXRhdmVsdmV0ICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg1LDEwMCxieSA9IDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQoKZ2dwbG90bHkocCkKICAKcAoKZ2dwbG90KGRhdGEgPSBkYXRhdmVsdmV0LCBhZXMoeCA9IEFnZSkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ImJsdWUiKSsgeGxpbShjKDIwLDEwMCkpCgogIAoKCmBgYAoKQW5vdGhlciBIaXN0b2dyYW0KCgpgYGB7cn0KCmRhdGF2ZWx2ZXQgJT4lCiAgZmlsdGVyKEFnZSA+PSAxMCkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgZ2dwbG90KGFlcyhBZ2UpKSArIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiIzAwMjg0NSIsIGJpbnMgPSAyMCkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwMDAsMjAwMCkpIAoKYGBgCkFnZSBkaXN0cmlidXRpb24gYnkgZ2VuZGVyIAoKYGBge3J9CiNBZ2UgR2VuZGVyIGZpbHRlcmVkIG91dCBiZWxvdyAxNSBhbmQgYWJvdmUgOTAgLSBhbHNvIHJlbW92ZWQgVSBYIHRoZSB3ZWlyZCB2YWx1ZXMgCmRhdGF2ZWx2ZXQgJT4lCiAgZmlsdGVyKEFnZSA+PSAxNSkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgbXV0YXRlKFNleCA9IGFzLmZhY3RvcihTZXgpKSAlPiUKICBmaWx0ZXIoU2V4ICE9ICJVIikgJT4lCiAgZmlsdGVyKFNleCAhPSAiWCIpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gU2V4KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUpICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkFnZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDUwMDAwLDIwMDApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5OTczIiwgIiMwMGNmY2MiKSkKYGBgCgpEb25vciBhZ2UgZGlzdHJpYnV0aW9uIGJ5IG1hcml0YWwgc3RhdHVzIAoKYGBge3J9CiNBZ2UgTWFyaXRhbCBTdGF0dXMKZGF0YXZlbHZldCAlPiUKICBmaWx0ZXIoQWdlID49IDIwKSAlPiUKICBmaWx0ZXIoQWdlIDw9IDg1KSAlPiUKICBnZ3Bsb3QoYWVzKEFnZSwgZmlsbCA9IE1hcnJpZWQpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyNSkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIGJ5IE1hcml0YWwgU3RhdHVzIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsNTAwMDAsMjAwMCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmZjk5NzMiLCAiIzAwY2ZjYyIpKQpgYGAKCkxpbmVhciBNb2RlbCAKCmBgYHtyfQojVGhlc2Ugd2lsbCBmb2N1cyBvbiBwcmVkaWN0aW5nIHdoZXRoZXIgYSBjb25zdGl0dWVudCBpcyBhIGRvbm9yIG9yIG5vbi1kb25vci4gCgoKbW9kMWxtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiBNYXJyaWVkX3NpbXBsZSwKICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbikKCm1vZDJsbSA8LSBsbSggVG90YWwuR2l2aW5nLlllYXJzIH4gTGlmZXRpbWUuR2l2aW5nLAogICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKbW9kM2xtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiByZWdpb24sCiAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KG1vZDFsbSkKc3VtbWFyeShtb2QybG0pCnN1bW1hcnkobW9kM2xtKQojaW5jcmVhc2luZyB0aGUgZ2l2aW5nIHllYXIgb25lIHllYXIgaW5jcmVhc2UgdG90YWwgZ2l2aW5nIGJ5IDAuMDAzNQoKCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coSEguTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+cmVnaW9uKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIlJlZ2lvbiIpCgoKZ2dwbG90KGRhdGEgPSBkYXRhX3RyYWluLCBhZXMoeCA9IEFnZSwgeSA9IGxvZyhISC5MaWZldGltZS5HaXZpbmcpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5ubWJfZGVncmVlKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIk51bWJlciBvZiBEZWdyZWVzIikKCgpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkZpcnN0LkdpZnQuQW1vdW50KSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+ZG9ub3JzZWdfZmN0KSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIkRvbm9yIFNlZ21lbnQiKQoKI1RoaXMgcGxvdCBhY3R1YWxseSBoYXMgc29tZSBpbnRlcmVzdGluZyByZXN1bHRzCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+Tm9fb2ZfQ2hpbGRyZW4pICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiIyBDaGlsZHJlbiIpCgoKZGF0YV90cmFpbiAlPiUgCiAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lIAogIGdsaW1wc2UoKQoKCmBgYAoKCgpNT1JFIE1PREVMUwoKQmlnIGxvZ2lzdGljIG1vZGVsCgpgYGB7cn0KCiMgU2V0IGZhbWlseSB0byBiaW5vbWlhbCB0byBzZXQgbG9naXN0aWMgZnVuY3Rpb24KIyBSdW4gdGhlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQKCmRvbm9yX2xvZ2l0MSA8LQogIGdsbShoaC5saWZldGltZS5naXZpbmdfZmN0IH4gTWFycmllZF9zaW1wbGUsCiAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsCiAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKc3VtbWFyeShkb25vcl9sb2dpdDEpCgoKZG9ub3JfbG9naXQyIDwtCiAgZ2xtKGhoLmxpZmV0aW1lLmdpdmluZ19mY3QgfiBOb19vZl9DaGlsZHJlbiwKICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KGRvbm9yX2xvZ2l0MikKCgpgYGAKCgoKYGBge3IgbG9nfQoKCiNzdW1tYXJ5KGRhdGFfdHJhaW4kbWFqb3JfZ2lmdGVyKQojQXNzaWdubWVudF9mbGFnIHRha2VuIG91dCAtIG1heSBhZGQgYmFjawoKZG9ub3JfbG9naXQzIDwtCiAgZ2xtKG1ham9yX2dpZnRlciB+IE1hcnJpZWQgKyBOb19vZl9DaGlsZHJlbiArIGRvbm9yc2VnX3NpbXBsZSArICBUb3RhbC5HaXZpbmcuWWVhcnMgKyBubWJfZGVncmVlLAogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkoZG9ub3JfbG9naXQzKQpleHAoZG9ub3JfbG9naXQzJGNvZWZmaWNpZW50cykKCiN0cmFpbmluZyBwcmVkaWN0aW9ucyBmb3IgaW4gc2FtcGxlIHByZWRzIApwcmVkc190cmFpbiA8LSBwcmVkaWN0KGRvbm9yX2xvZ2l0MywgbmV3ZGF0YSA9IGRhdGFfdHJhaW4sIHR5cGUgPSAicmVzcG9uc2UiKSAKCiN0ZXN0IHByZWRpY3RzIGZvciBPT1MgKG91dCBvZiBzYW1wbGUpCnByZWRzX3Rlc3QgPC0gcHJlZGljdChkb25vcl9sb2dpdDMsIG5ld2RhdGEgPSBkYXRhX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQoKaGVhZChwcmVkc190cmFpbikKaGVhZChwcmVkc190ZXN0KQoKCgpyZXN1bHRzX3RyYWluIDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdHJhaW4gICAlPiUgc2VsZWN0KG1ham9yX2dpZnRlcikgJT4lIAogICAgbXV0YXRlKG1ham9yX2dpZnRlciA9IGFzLm51bWVyaWMobWFqb3JfZ2lmdGVyKSksCiAgYENsYXNzMWAgPSAgcHJlZHNfdHJhaW4sCiAgYHR5cGVgID0gcmVwKCJ0cmFpbiIsbGVuZ3RoKHByZWRzX3RyYWluKSkKKQoKcmVzdWx0c190ZXN0IDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdGVzdCAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgCiAgICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gYXMubnVtZXJpYyhtYWpvcl9naWZ0ZXIpKSwKICBgQ2xhc3MxYCA9ICBwcmVkc190ZXN0LAogIGB0eXBlYCA9IHJlcCgidGVzdCIsbGVuZ3RoKHByZWRzX3Rlc3QpKQopCgpyZXN1bHRzIDwtIGJpbmRfcm93cyhyZXN1bHRzX3RyYWluLHJlc3VsdHNfdGVzdCkKCmRpbShyZXN1bHRzX3RyYWluKQpkaW0ocmVzdWx0c190ZXN0KQpkaW0ocmVzdWx0cykKCmxpYnJhcnkoJ3Bsb3RST0MnKQoKcF9wbG90IDwtCiAgZ2dwbG90KHJlc3VsdHMsCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArCiAgZ2VvbV9yb2MobGFiZWxzaXplID0gMi41LAogICAgICAgICAgICNUb29rIHRoZSBsYWJlbHNpemUgZG93biB0byBhdm9pZCBjdXRvZmYKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKICNXZSByZW1vdmVkIHNvbWUgb2YgdGhlIGN1dG9mZnMgdG8gYXZvaWQgdGhlIG1hc2h1cCBuZWFyIHRoZSBvcmlnaW4uCgogICNDaGFuZ2VkIHRoZSB0aGVtZSB0byBhdm9pZCBjdXRvZmYgcGxvdCB2YWx1ZXMuCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyAKICBsYWJzKHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsIAogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArCiAgICAgIGdndGl0bGUoIlJPQyBQbG90OiBUcmFpbmluZyBhbmQgVGVzdCIpCnByaW50KHBfcGxvdCkgCgoKcF90cmFpbiA8LQogIGdncGxvdChyZXN1bHRzX3RyYWluLAogICAgICAgICBhZXMobSA9IENsYXNzMSwgZCA9IG1ham9yX2dpZnRlciwgY29sb3IgPSB0eXBlKSkgKwogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKIAogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgCiAgbGFicyh4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLCAKICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKwogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQoKcF90ZXN0IDwtCiAgZ2dwbG90KHJlc3VsdHNfdGVzdCwKICAgICAgICAgYWVzKG0gPSBDbGFzczEsIGQgPSBtYWpvcl9naWZ0ZXIsIGNvbG9yID0gdHlwZSkpICsKICBnZW9tX3JvYyhsYWJlbHNpemUgPSAzLjUsCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArCiAKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE2KSArIAogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIpICsKICAgICAgZ2d0aXRsZSgiUk9DIFBsb3Q6IFRyYWluaW5nIGFuZCBUZXN0IikKCmBgYAoKYGBge3J9Cgojc3VtbWFyeShkb25vcl9sb2dpdDMpCiNjb2VmKGRvbm9yX2xvZ2l0MykKCgojQ2FsY3VsYXRpbmcgQVVDIG9mIGJvdGgKcHJpbnQoY2FsY19hdWMocF90cmFpbikkQVVDKQpwcmludChjYWxjX2F1YyhwX3Rlc3QpJEFVQykKYGBgCgpSSURHRQoKYGBge3J9CgpsaWJyYXJ5KCdnbG1uZXQnKQpsaWJyYXJ5KCdnbG1uZXRVdGlscycpCgpyaWRnZV9maXQxIDwtIGN2LmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcuUGx1cyB+IHNleF9mY3QgKyBkb25vcnNlZ19mY3QgKyBOb19vZl9DaGlsZHJlbiwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDApCgojQWxwaGEgMCBzZXRzIHRoZSBSaWRnZQpwcmludChyaWRnZV9maXQxKQpwcmludChyaWRnZV9maXQxJGxhbWJkYS5taW4pCgpwcmludChyaWRnZV9maXQxJGxhbWJkYS4xc2UpCnBsb3QocmlkZ2VfZml0MSkKCmBgYAoKQklHIFJJREdFCgpgYGB7cn0KCmxpYnJhcnkoJ2dsbW5ldCcpCmxpYnJhcnkoJ2dsbW5ldFV0aWxzJykKCmRhdGFfdHJhaW4gJT4lIG1hcChsZXZlbHMpICU+JSBtYXAobGVuZ3RoKQoKcmlkZ2VfZml0MiA8LSBjdi5nbG1uZXQoSEguTGlmZXRpbWUuR2l2aW5nLlBsdXMgfiAuLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCkKCiNBbHBoYSAwIHNldHMgdGhlIFJpZGdlCnByaW50KHJpZGdlX2ZpdDIpCnByaW50KHJpZGdlX2ZpdDIkbGFtYmRhLm1pbikKCnByaW50KHJpZGdlX2ZpdDIkbGFtYmRhLjFzZSkKcGxvdChyaWRnZV9maXQyKQoKYGBgCgoKTEFTU08KCmBgYHtyfQoKI1VzaW5nIGN2LmdsbW5ldCBmcm9tIGNsYXNzCiNscyhkYXRhX3RyYWluKSAKI2lzLmZhY3RvcihkYXRhX3RyYWluJG1ham9yX2dpZnRlcikKI2dsaW1wc2UoZGF0YV90cmFpbiRMaWZldGltZS5HaXZpbmcpCgojZGF0YV90cmFpbiAlPiUgCiMgIHNlbGVjdF9pZihpcy5mYWN0b3IpICU+JSAKIyAgZ2xpbXBzZSgpCgoKCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KGdsbW5ldFV0aWxzKQpsYXNzb19maXQgPC0gY3YuZ2xtbmV0KEhILkxpZmV0aW1lLkdpdmluZy5QbHVzIH4gc2V4X2ZjdCArIGpvYnRpdGxlX3NpbXBsZSArIG5tYl9kZWdyZWUgKyBzY2hvb2wxX3NpbXBsZSArIGhoZmlyc3RnaWZ0X3NpbXBsZSArIG1hajFfc2ltcGxlICsgZG9ub3JzZWdfc2ltcGxlICsgTm9fb2ZfQ2hpbGRyZW4gKyBNYXJyaWVkLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICNBbHBoYSAxIGZvciBsYXNzbwogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSkKCgpwcmludChsYXNzb19maXQkbGFtYmRhLm1pbikKIwpwcmludChsYXNzb19maXQkbGFtYmRhLjFzZSkKCnBsb3QobGFzc29fZml0KQoKYGBgCgoKCgpgYGB7cn0KCmNvZWYobGFzc29fZml0KQojRGVmYXVsdCBzZXR0aW5nIGlzIGxhbWJkYS4xc2UKCiNGcm9tIHRoZSBib29rIC0gc2hvd2luZyBjb252ZXJnZW5jZSB3aXRoIGxhbWJkYSB2YWx1ZXMKcGxvdChsYXNzb19maXQkZ2xtbmV0LmZpdCwgeHZhcj0ibGFtYmRhIikKI2FibGluZSh2PWxvZyhjKGxhc3NvX2ZpdCRsYW1iZGEubWluLCBsYXNzb19maXQkbGFtYmRhLjFzZSkpLCBsdHk9MikKCmBgYAoKCgoKCmBgYHtyfQoKZW5ldF9tb2QgPC0gY3ZhLmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcuUGx1cyB+IHNleF9mY3QgKyBqb2J0aXRsZV9zaW1wbGUgKyBubWJfZGVncmVlICsgc2Nob29sMV9zaW1wbGUgKyBoaGZpcnN0Z2lmdF9zaW1wbGUgKyBtYWoxX3NpbXBsZSArIGRvbm9yc2VnX3NpbXBsZSArIE5vX29mX0NoaWxkcmVuICsgTWFycmllZCwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IHNlcSgwLDEsIGJ5ID0gMC4xKSkKCnByaW50KGVuZXRfbW9kKQpwbG90KGVuZXRfbW9kKQoKCmBgYAoKRUxBU1RJQ05FVAoKYGBge3IgZWxhc3RpY25ldH0KCm1pbmxvc3NwbG90KGVuZXRfbW9kLCAKICAgICAgICAgICAgY3YudHlwZSA9ICJtaW4iKQoKZ2V0X2FscGhhIDwtIGZ1bmN0aW9uKGZpdCkgewogIGFscGhhIDwtIGZpdCRhbHBoYQogIGVycm9yIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgCiAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKG1vZCkge21pbihtb2QkY3ZtKX0pCiAgYWxwaGFbd2hpY2gubWluKGVycm9yKV0KfQoKZ2V0X21vZGVsX3BhcmFtcyA8LSBmdW5jdGlvbihmaXQpIHsKICBhbHBoYSA8LSBmaXQkYWxwaGEKICBsYW1iZGFNaW4gPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBgW1tgLCAibGFtYmRhLm1pbiIpCiAgbGFtYmRhU0UgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBgW1tgLCAibGFtYmRhLjFzZSIpCiAgZXJyb3IgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBmdW5jdGlvbihtb2QpIHttaW4obW9kJGN2bSl9KQogIGJlc3QgPC0gd2hpY2gubWluKGVycm9yKQogIGRhdGEuZnJhbWUoYWxwaGEgPSBhbHBoYVtiZXN0XSwgbGFtYmRhTWluID0gbGFtYmRhTWluW2Jlc3RdLAogICAgICAgICAgICAgbGFtYmRhU0UgPSBsYW1iZGFTRVtiZXN0XSwgZXJvciA9IGVycm9yW2Jlc3RdKQp9CgpiZXN0X2FscGhhIDwtIGdldF9hbHBoYShlbmV0X21vZCkKcHJpbnQoYmVzdF9hbHBoYSkKZ2V0X21vZGVsX3BhcmFtcyhlbmV0X21vZCkKCmJlc3RfbW9kIDwtIGVuZXRfbW9kJG1vZGxpc3RbW3doaWNoKGVuZXRfbW9kJGFscGhhID09IGJlc3RfYWxwaGEpXV0KCnByaW50KGJlc3RfbW9kKQoKbWlubG9zc3Bsb3QoZW5ldF9tb2QsIGN2LnR5cGUgPSAibWluIikKCmBgYAoKUmlkZ2VzIHBsb3QgLSBjb3VsZCBiZSB1c2VmdWwgZm9yIHBsb3R0aW5nIGRvbmF0aW9ucyB2cyBkb25vciBzZWdtZW50CgpgYGB7cn0KCmxpYnJhcnkoJ2dncmlkZ2VzJykKCnN1bW1hcnkoZGF0YV90cmFpbiR2YXJpYWJsZSkKCmdncGxvdChkYXRhX3RyYWluLCBhZXMoeCA9IEhILkxpZmV0aW1lLkdpdmluZywgeSA9IHJlZ2lvbikpICsgZ2VvbV9kZW5zaXR5X3JpZGdlcyhyZWxfbWluX2hlaWdodCA9IDAuMDA1KSArIHhsaW0oYygyNTAwMCwgMTAwMDAwKSkgKyAKICAgICAgZ2d0aXRsZSgiSEggTGlmZXRpbWUgR2l2aW5nIGJ5IFJlZ2lvbiIpCgpgYGAKCmBgYHtyfQoKbGlicmFyeSgnY29ycnBsb3QnKQoKI3JlbW92aW5nIElEIHppcCBhbmQgbm9ubnVtZXJpYyAKY29ycnBsb3RfZGF0YSA8LSBkYXRhY2xlYW5bLWMoMTo0OCw1Mjo1Niw1ODo2MCw2Myw2Njo2Nyw3MDo3Miw3NDo4MSw4MzoxMzIpXQoKI0NvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gbnVtZXJpYyBkYXRhIHR5cGUKY29udmVydF9mYWMybnVtIDwtIGZ1bmN0aW9uKHgpewogIGFzLm51bWVyaWMoYXMuZmFjdG9yKHgpKQp9Cgpjb3JycGxvdF9kYXRhIDwtIG11dGF0ZV9hdChjb3JycGxvdF9kYXRhLAogICAgICAgICAgICAgICAgICAgICAudmFycyA9IGMoMToxMiksCiAgICAgICAgICAgICAgICAgICAgIC5mdW5zID0gY29udmVydF9mYWMybnVtKQojbWFraW5nIGEgbWF0cml4CmNkX2NvciA8LSBjb3IoY29ycnBsb3RfZGF0YSkKCiNjcmVhdGluZyBjb3JyZWxhdGlvbgpjb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjQkI0NDAwIiwgIiNFRTk5OTAiLCAKICAiI0ZGRkZGRiIsICIjNzdBQUVFIiwgIiM0NDc3QkIiKSkKY29ycnBsb3QoY2RfY29yLCBtZXRob2Q9ImNvbG9yIiwgY29sPWNvbCgxMDApLAogIHR5cGU9Imxvd2VyIiwgYWRkQ29lZi5jb2wgPSAiYmxhY2siLAogIHRsLnBvcz0ibHQiLCB0bC5jb2w9ImJsYWNrIiwgCiAgdGwuY2V4PTAuNywgdGwuc3J0PTQ1LCAKICBudW1iZXIuY2V4PTAuNywKICBkaWFnPUZBTFNFKQoKI2NvcnJlbGF0aW9uIG1hdHJpeAojIHBhaXJzKH5BZ2UgKyBNb250aHMuU2luY2UuTGFzdC5HaWZ0ICsgZG9ub3JzZWdfZmN0ICsgCiMgICAgIG5tYl9kZWdyZWUgKyBOb19vZl9DaGlsZHJlbiArIEhILkZpcnN0LkdpZnQuQWdlICsgSEguRmlyc3QuR2lmdC5BbW91bnQgKyBUb3RhbC5HaXZpbmcuWWVhcnMsCiMgICAgIGNvbCA9IGNvcnJwbG90X2RhdGEkSEguTGlmZXRpbWUuR2l2aW5nLAojICAgICBkYXRhID0gY29ycnBsb3RfZGF0YSwgCiMgICAgIG1haW4gPSAiRG9ub3IgU2NhdHRlciBQbG90IE1hdHJpeCIpCgojd29ydGhsZXNzLi4gCgpnZ3Bsb3QoZGF0YSA9IGNvcnJwbG90X2RhdGEsIGFlcyh4ID0gbm1iX2RlZ3JlZSwgeSA9IEhILkxpZmV0aW1lLkdpdmluZykpICsgCiAgZ2VvbV9wb2ludChhcGxoYSA9IDEvMTApKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0icmVkIikgCgpgYGAKCgpSYW5kb20gRm9yZXN0CgpgYGB7cn0KCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdCcpCgpyZl9maXRfZG9ub3IgPC0gcmFuZG9tRm9yZXN0KExpZmV0aW1lLkdpdmluZyB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSBjbGFzc2lmaWNhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gNywKICAgICAgICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5yb3VnaGZpeCwKICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlPVRSVUUKICAgICAgICAgICAgICAgICAgICAgICApCgpwcmludChyZl9maXRfZG9ub3IpCgoKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9Cgp2YXJJbXBQbG90KHJmX2ZpdF9kb25vciwgc29ydCA9IFRSVUUsIAogICAgICAgICAgIG4udmFyID0gNSwKICAgICAgICAgICB0eXBlID0gMiwgY2xhc3MgPSBOVUxMLCBzY2FsZSA9IFRSVUUsIAogICAgICAgICAgIG1haW4gPSBkZXBhcnNlKHN1YnN0aXR1dGUocmZfZml0X2Rvbm9yKSkpCgoKYGBgCgpgYGB7cn0KCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdEV4cGxhaW5lcicpCgpwbG90X21pbl9kZXB0aF9kaXN0cmlidXRpb24oCiAgcmZfZml0X2Rvbm9yLAogIGsgPSAxMCwKICBtaW5fbm9fb2ZfdHJlZXMgPSAwLAogIG1lYW5fc2FtcGxlID0gInRvcF90cmVlcyIsCiAgbWVhbl9zY2FsZSA9IEZBTFNFLAogIG1lYW5fcm91bmQgPSAyLAogIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIG1pbmltYWwgZGVwdGggYW5kIGl0cyBtZWFuIgopCgpgYGAKCgoKCmBgYHtyfQojU3BsaXR0aW5nIENhdGVnb3J5IG91dCB0byBjaGVjayBpZiB0aGUgY2F0ZWdvcnkgaXMgdXNlZnVsIGZvciBhbmFseXNpcwpkYXRhX2NhdGVnb3J5X3NwbGl0X291dCA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKENhdGVnb3J5LkNvZGVzID0gdHJpbShzdHJzcGxpdChhcy5jaGFyYWN0ZXIoQ2F0ZWdvcnkuQ29kZXMpLCAifCIsIGZpeGVkID0gVFJVRSkpKSAlPiUKICB1bm5lc3QoQ2F0ZWdvcnkuQ29kZXMpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ2F0ZWdvcnkuQ29kZXMsdmFsdWVzX2Zyb20gPUNhdGVnb3J5LkNvZGVzLCB2YWx1ZXNfZm4gPSBsZW5ndGgpCgoKYGBgCg==